add path selection page

new update page WIP
This commit is contained in:
IsWaffle 2024-06-28 21:35:27 -04:00
parent 0c6ce9e681
commit 1dc4202353
11 changed files with 266 additions and 3 deletions

View File

@ -19,6 +19,9 @@
<entry key="SPTInstaller/CustomControls/UpdateButton.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/UpdateButton.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/UpdateInfoCard.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/UpdateInfoCard.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/DetailedPreChecksView.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/Views/DetailedPreChecksView.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/InstallPathSelectionView.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/InstallView.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/InstallerUpdateView.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/MainWindow.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/Views/MainWindow.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/MessageView.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/Views/MessageView.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/Views/PreChecksView.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/Views/PreChecksView.axaml" value="SPTInstaller/SPTInstaller.csproj" />

View File

@ -48,5 +48,8 @@
<PathGeometry x:Key="Bug" <PathGeometry x:Key="Bug"
Figures="m 12.25 0 a 0.75 0.75 0 0 1 0.743 0.648 L 13 0.75 v 0.752 c 0 0.633 -0.196 1.22 -0.53 1.704 a 3.75 3.75 0 0 1 2.521 3.29 h 0.256 a 2.25 2.25 0 0 0 2.24 -2.259 L 17.481 2.752 a 0.750006 0.750006 0 0 1 1.5 -0.006 l 0.007 1.485 a 3.75 3.75 0 0 1 -3.536 3.76 L 15.238 7.997 L 15 7.996 v 1.502 h 4.253 a 0.75 0.75 0 0 1 0.743 0.649 l 0.007 0.102 a 0.75 0.75 0 0 1 -0.648 0.743 l -0.102 0.007 H 15 v 1.999 h 0.238 l 0.214 0.007 a 3.75 3.75 0 0 1 3.531 3.56 l 0.005 0.2 l -0.007 1.485 a 0.75 0.75 0 0 1 -1.493 0.095 l -0.007 -0.102 l 0.007 -1.485 a 2.25 2.25 0 0 0 -2.087 -2.253 l -0.154 -0.006 h -0.476 a 5.002 5.002 0 0 1 -9.542 0 H 4.74 A 2.25 2.25 0 0 0 2.5 16.758 l 0.005 1.485 a 0.750008 0.750008 0 1 1 -1.5 0.007 L 1 16.764 a 3.75 3.75 0 0 1 3.535 -3.76 L 4.75 12.999 L 5 12.998 v -2 H 0.75 A 0.75 0.75 0 0 1 0.007 10.35 L 0 10.249 A 0.75 0.75 0 0 1 0.648 9.506 L 0.75 9.499 L 5 9.498 V 7.996 H 4.75 L 4.535 7.991 A 3.75 3.75 0 0 1 1.005 4.431 L 1 4.23 L 1.006 2.745 A 0.75 0.75 0 0 1 2.5 2.649 L 2.506 2.751 L 2.5 4.237 A 2.25 2.25 0 0 0 4.587 6.491 L 4.741 6.497 H 5.009 A 3.753 3.753 0 0 1 7.53 3.205 A 2.968 2.968 0 0 1 7.006 1.711 L 7 1.502 V 0.75 A 0.75 0.75 0 0 1 8.493 0.648 L 8.5 0.75 v 0.752 a 1.5 1.5 0 0 0 2.993 0.145 L 11.5 1.502 V 0.75 A 0.75 0.75 0 0 1 12.25 0 Z" Figures="m 12.25 0 a 0.75 0.75 0 0 1 0.743 0.648 L 13 0.75 v 0.752 c 0 0.633 -0.196 1.22 -0.53 1.704 a 3.75 3.75 0 0 1 2.521 3.29 h 0.256 a 2.25 2.25 0 0 0 2.24 -2.259 L 17.481 2.752 a 0.750006 0.750006 0 0 1 1.5 -0.006 l 0.007 1.485 a 3.75 3.75 0 0 1 -3.536 3.76 L 15.238 7.997 L 15 7.996 v 1.502 h 4.253 a 0.75 0.75 0 0 1 0.743 0.649 l 0.007 0.102 a 0.75 0.75 0 0 1 -0.648 0.743 l -0.102 0.007 H 15 v 1.999 h 0.238 l 0.214 0.007 a 3.75 3.75 0 0 1 3.531 3.56 l 0.005 0.2 l -0.007 1.485 a 0.75 0.75 0 0 1 -1.493 0.095 l -0.007 -0.102 l 0.007 -1.485 a 2.25 2.25 0 0 0 -2.087 -2.253 l -0.154 -0.006 h -0.476 a 5.002 5.002 0 0 1 -9.542 0 H 4.74 A 2.25 2.25 0 0 0 2.5 16.758 l 0.005 1.485 a 0.750008 0.750008 0 1 1 -1.5 0.007 L 1 16.764 a 3.75 3.75 0 0 1 3.535 -3.76 L 4.75 12.999 L 5 12.998 v -2 H 0.75 A 0.75 0.75 0 0 1 0.007 10.35 L 0 10.249 A 0.75 0.75 0 0 1 0.648 9.506 L 0.75 9.499 L 5 9.498 V 7.996 H 4.75 L 4.535 7.991 A 3.75 3.75 0 0 1 1.005 4.431 L 1 4.23 L 1.006 2.745 A 0.75 0.75 0 0 1 2.5 2.649 L 2.506 2.751 L 2.5 4.237 A 2.25 2.25 0 0 0 4.587 6.491 L 4.741 6.497 H 5.009 A 3.753 3.753 0 0 1 7.53 3.205 A 2.968 2.968 0 0 1 7.006 1.711 L 7 1.502 V 0.75 A 0.75 0.75 0 0 1 8.493 0.648 L 8.5 0.75 v 0.752 a 1.5 1.5 0 0 0 2.993 0.145 L 11.5 1.502 V 0.75 A 0.75 0.75 0 0 1 12.25 0 Z"
FillRule="NonZero" /> FillRule="NonZero" />
<PathGeometry x:Key="OpenFolder" Figures="M 2.2731724 14.474999 C 2.5381753 14.186249 3.2824783 12.195001 3.9271792 10.05 5.6676413 4.2592679 4.7621113 4.8000009 12.719033 4.8000009 c 5.6684 0 6.78597 0.072438 7.12511 0.4618343 0.332844 0.3821726 0.17704 1.1971998 -0.903259 4.7250006 -0.763041 2.4917722 -1.52781 4.4189802 -1.840552 4.6381652 C 16.708149 14.899859 14.592619 15 9.1783054 15 2.1694393 15 1.8160107 14.973129 2.2731724 14.474999 Z M 0.36305228 14.025959 C 0.11166709 13.786409 0 11.721164 0 7.3114288 0 1.9218189 0.0760474 0.8703905 0.49472143 0.47142828 0.8806724 0.10364926 1.7051307 0 4.2446088 0 7.4749739 0 7.5058294 0.00685701 8.2944922 0.89999983 L 9.0892098 1.8 h 3.6407872 c 3.221023 0 3.71338 0.069177 4.270431 0.5999996 0.346306 0.3300009 0.629646 0.802501 0.629646 1.0500009 0 0.3838238 -0.858607 0.4500002 -5.83853 0.4500002 -5.6986082 0 -5.856156 0.016794 -6.5739181 0.7007613 C 4.8131633 4.9861817 4.2426547 6.0999322 3.9498292 7.0757619 2.3566037 12.385128 1.8127023 13.81777 1.2887903 14.084957 c -0.37832867 0.192941 -0.68163535 0.173611 -0.92573802 -0.059 z"
FillRule="NonZero" />
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -134,12 +134,19 @@ public static class FileHelper
} }
} }
/// <summary>
/// Check if a path is problematic
/// </summary>
/// <param name="path">The path the check</param>
/// <param name="failedCheck">The check that failed</param>
/// <returns>Returns true if the path is bad, otherwise false</returns>
public static bool CheckPathForProblemLocations(string path, out PathCheck failedCheck) public static bool CheckPathForProblemLocations(string path, out PathCheck failedCheck)
{ {
failedCheck = new(); failedCheck = new();
var problemPaths = new List<PathCheck>() var problemPaths = new List<PathCheck>()
{ {
new("SteamApps", PathCheckType.EndsWith, PathCheckAction.Warn),
new("Documents", PathCheckType.EndsWith, PathCheckAction.Warn), new("Documents", PathCheckType.EndsWith, PathCheckAction.Warn),
new("Desktop", PathCheckType.EndsWith, PathCheckAction.Deny), new("Desktop", PathCheckType.EndsWith, PathCheckAction.Deny),
new("Battlestate Games", PathCheckType.Contains, PathCheckAction.Deny), new("Battlestate Games", PathCheckType.Contains, PathCheckAction.Deny),

View File

@ -0,0 +1,136 @@
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage;
using ReactiveUI;
using SPTInstaller.Helpers;
using SPTInstaller.Models;
namespace SPTInstaller.ViewModels;
public class InstallPathSelectionViewModel : ViewModelBase
{
private bool _debugging = false;
private InternalData _data;
private string _selectedPath;
public string SelectedPath
{
get => _selectedPath;
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
}
private bool _validPath;
public bool ValidPath
{
get => _validPath;
set => this.RaiseAndSetIfChanged(ref _validPath, value);
}
private string _errorMessage;
public string ErrorMessage
{
get => _errorMessage;
set => this.RaiseAndSetIfChanged(ref _errorMessage, value);
}
public InstallPathSelectionViewModel(IScreen host, bool debugging) : base(host)
{
_debugging = debugging;
_data = ServiceHelper.Get<InternalData?>() ?? throw new Exception("Failed to get internal data");
SelectedPath = Environment.CurrentDirectory;
ValidPath = false;
AdjustInstallPath();
}
public async Task SelectFolderCommand()
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (desktop.MainWindow == null)
{
return;
}
var startingFolderPath = Directory.Exists(SelectedPath) ? SelectedPath : Environment.CurrentDirectory;
var suggestedFolder = await desktop.MainWindow.StorageProvider.TryGetFolderFromPathAsync(startingFolderPath);
var selections = await desktop.MainWindow.StorageProvider.OpenFolderPickerAsync(
new FolderPickerOpenOptions()
{
AllowMultiple = false,
SuggestedStartLocation = suggestedFolder,
Title = "Select a folder to install SPT into"
});
SelectedPath = selections.First().Path.AbsolutePath.Replace("/", "\\");
}
}
public void ValidatePath()
{
if (String.IsNullOrEmpty(SelectedPath))
{
ErrorMessage = "Please provide an install path";
ValidPath = false;
return;
}
if (FileHelper.CheckPathForProblemLocations(SelectedPath, out var failedCheck))
{
if (failedCheck.CheckType == PathCheckType.EndsWith)
{
ErrorMessage = "This folder can be install in, but only in a subdirectory";
ValidPath = false;
return;
}
if (failedCheck.CheckAction == PathCheckAction.Deny)
{
ErrorMessage = $"Sorry, you cannot install in {failedCheck.Target}";
ValidPath = false;
return;
}
}
ValidPath = true;
}
private void AdjustInstallPath()
{
if (FileHelper.CheckPathForProblemLocations(SelectedPath, out var failedCheck))
{
switch (failedCheck.CheckType)
{
case PathCheckType.EndsWith:
SelectedPath = Path.Join(Environment.CurrentDirectory, "SPT");
break;
case PathCheckType.Contains:
case PathCheckType.DriveRoot:
SelectedPath = Path.Join(Directory.GetDirectoryRoot(Environment.CurrentDirectory), "SPT");
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public async Task NextCommand()
{
if (FileHelper.CheckPathForProblemLocations(SelectedPath, out _))
{
return;
}
_data.TargetInstallPath = SelectedPath;
NavigateTo(new PreChecksViewModel(HostScreen, _debugging));
}
}

View File

@ -0,0 +1,12 @@
using ReactiveUI;
namespace SPTInstaller.ViewModels;
public class InstallerUpdateViewModel : ViewModelBase
{
private bool _debugging;
public InstallerUpdateViewModel(IScreen Host, bool debugging) : base(Host)
{
_debugging = debugging;
}
}

View File

@ -31,7 +31,7 @@ public class MainWindowViewModel : ReactiveObject, IActivatableViewModel, IScree
Log.Information("System Language: {iso} - {name}", uiCulture.TwoLetterISOLanguageName, uiCulture.DisplayName); Log.Information("System Language: {iso} - {name}", uiCulture.TwoLetterISOLanguageName, uiCulture.DisplayName);
Router.Navigate.Execute(new PreChecksViewModel(this, debugging)); Router.Navigate.Execute(new InstallerUpdateViewModel(this, debugging));
} }
public void CloseCommand() public void CloseCommand()

View File

@ -138,7 +138,6 @@ public class PreChecksViewModel : ViewModelBase
data.OriginalGamePath = PreCheckHelper.DetectOriginalGamePath(); data.OriginalGamePath = PreCheckHelper.DetectOriginalGamePath();
data.TargetInstallPath = Environment.CurrentDirectory;
InstallPath = data.TargetInstallPath; InstallPath = data.TargetInstallPath;
Log.Information($"Install Path: {FileHelper.GetRedactedPath(InstallPath)}"); Log.Information($"Install Path: {FileHelper.GetRedactedPath(InstallPath)}");
@ -224,7 +223,8 @@ public class PreChecksViewModel : ViewModelBase
{ {
try try
{ {
var installerPath = Path.Join(_installPath, "SPTInstaller.exe"); var installerPath = Path.Join(Environment.CurrentDirectory, "SPTInstaller.exe");
Process.Start(new ProcessStartInfo() Process.Start(new ProcessStartInfo()
{ {
FileName = installerPath, FileName = installerPath,
@ -302,6 +302,7 @@ public class PreChecksViewModel : ViewModelBase
var SPTReleaseInfo = var SPTReleaseInfo =
JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(SPTReleaseInfoFile.FullName)); JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(SPTReleaseInfoFile.FullName));
if (SPTReleaseInfo == null) if (SPTReleaseInfo == null)
{ {
InstallButtonText = "Could not parse latest SPT release"; InstallButtonText = "Could not parse latest SPT release";

View File

@ -0,0 +1,63 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.Views.InstallPathSelectionView">
<Grid RowDefinitions="10,*,Auto,*,10" ColumnDefinitions="10,*,Auto,10">
<!-- Path Controls Grid -->
<Grid Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto"
VerticalAlignment="Center"
>
<Label Grid.Row="0" Grid.Column="0" Content="Install Folder Path" FontSize="20"/>
<TextBox Grid.Row="1" Grid.Column="0"
TextChanged="TextBox_OnTextChanged"
Watermark="Where we dropping?"
FontSize="16"
Text="{Binding SelectedPath}"
Classes.hasErrors="{Binding !ValidPath}"
>
<TextBox.Styles>
<Style Selector="TextBox.hasErrors">
<Setter Property="Foreground" Value="Red"/>
</Style>
</TextBox.Styles>
</TextBox>
<Button Grid.Row="1" Grid.Column="1"
Margin="10 0 0 0"
Command="{Binding SelectFolderCommand}"
>
<StackPanel Orientation="Horizontal">
<Path Data="{StaticResource OpenFolder}" Fill="{Binding $parent[Button].Foreground}"
VerticalAlignment="Center"/>
<Label Content="Select Folder"/>
</StackPanel>
</Button>
</Grid>
<!-- Validation error text -->
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding ErrorMessage}"
TextWrapping="Wrap"
FontSize="16" Foreground="red" FontWeight="SemiBold"
IsVisible="{Binding !ValidPath}"
/>
<!-- Next button -->
<Button Grid.Row="3" Grid.Column="2"
MinWidth="100"
MinHeight="30"
FontSize="16"
FontWeight="SemiBold"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Classes="yellow"
Content="Next"
Command="{Binding NextCommand}"
IsEnabled="{Binding ValidPath}"
/>
</Grid>
</UserControl>

View File

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.ReactiveUI;
using SPTInstaller.ViewModels;
namespace SPTInstaller.Views;
public partial class InstallPathSelectionView : ReactiveUserControl<InstallPathSelectionViewModel>
{
public InstallPathSelectionView()
{
InitializeComponent();
}
private void TextBox_OnTextChanged(object? sender, TextChangedEventArgs e)
{
ViewModel?.ValidatePath();
}
}

View File

@ -0,0 +1,8 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.Views.InstallerUpdateView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,12 @@
using Avalonia.ReactiveUI;
using SPTInstaller.ViewModels;
namespace SPTInstaller.Views;
public partial class InstallerUpdateView : ReactiveUserControl<InstallerUpdateViewModel>
{
public InstallerUpdateView()
{
InitializeComponent();
}
}