udpate prechecks to use status spinner

This commit is contained in:
IsWaffle 2023-08-25 19:09:36 -04:00
parent d8d442c195
commit ada70d7957
10 changed files with 92 additions and 165 deletions

View File

@ -1,5 +1,6 @@
using Serilog;
using SharpCompress;
using SPTInstaller.CustomControls;
using SPTInstaller.Interfaces;
using SPTInstaller.Models;
using System.Collections.Generic;
@ -26,7 +27,7 @@ public class InstallController
Log.Information("-<>--<>- Running PreChecks -<>--<>-");
var requiredResults = new List<IResult>();
_preChecks.ForEach(x => x.IsPending = true);
_preChecks.ForEach(x => x.State = StatusSpinner.SpinnerState.Pending);
foreach (var check in _preChecks)
{

View File

@ -2,29 +2,10 @@
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"
xmlns:cc="using:SPTInstaller.CustomControls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.DetailedPreCheckItem"
Background="Transparent" MinHeight="100">
<UserControl.Styles>
<Style Selector="Arc.running">
<Setter Property="Stroke" Value="DodgerBlue"/>
</Style>
<Style Selector="Arc">
<Setter Property="Stroke" Value="Gray"/>
<Setter Property="IsVisible" Value="{Binding IsPending, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Style.Animations>
<Animation Duration="0:0:1" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="RotateTransform.Angle" Value="0"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</UserControl.Styles>
<Grid RowDefinitions="3,AUTO,3,*,30,3" ColumnDefinitions="3,*,AUTO,3" Margin="10">
<Border Grid.RowSpan="5" Grid.ColumnSpan="3"
@ -35,36 +16,13 @@
Background="{StaticResource AKI_Brush_DarkGrayBlue}" CornerRadius="8 8 0 0"
/>
<Grid Grid.Row="1" Grid.Column="1" ColumnDefinitions="22, AUTO" Margin="3">
<Canvas Margin="0 3 0 0"
IsVisible="{Binding !IsPending, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Ellipse Fill="White" Height="15" Width="15" Canvas.Top="3" Canvas.Left="3"
/>
<Path Name="iconPath" StrokeThickness="2"
Classes.passed="{Binding Passed, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
<Classes.failed>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsRequired"/>
<Binding Path="!Passed"/>
</MultiBinding>
</Classes.failed>
<Classes.warning>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!IsRequired"/>
<Binding Path="!Passed"/>
</MultiBinding>
</Classes.warning>
</Path>
</Canvas>
<Arc StartAngle="280" SweepAngle="80" Margin="0 3 0 0" StrokeThickness="3"
Width="20" Height="20" VerticalAlignment="Top"
Classes.running="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
<Grid Grid.Row="1" Grid.Column="1" ColumnDefinitions="AUTO, AUTO" Margin="3">
<cc:StatusSpinner State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Label Grid.Column="1"
Content="{Binding PreCheckName, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.bold="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.bold="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsStateConverter},
ConverterParameter=Running}"
/>
</Grid>

View File

@ -2,65 +2,29 @@
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"
xmlns:cc="using:SPTInstaller.CustomControls"
xmlns:convt="using:SPTInstaller.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.PreCheckItem">
<UserControl.Resources>
<convt:StatusSpinnerIsStateConverter x:Key="IsStateConverter"/>
</UserControl.Resources>
<UserControl.Styles>
<Style Selector="Arc.running">
<Setter Property="Stroke" Value="DodgerBlue"/>
</Style>
<Style Selector="Arc">
<Setter Property="Stroke" Value="Gray"/>
<Setter Property="IsVisible" Value="{Binding IsPending, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Style.Animations>
<Animation Duration="0:0:1" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="RotateTransform.Angle" Value="0"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="Label.bold">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</UserControl.Styles>
<Grid ColumnDefinitions="22, AUTO" Margin="3">
<Canvas Margin="0 3 0 0"
IsVisible="{Binding !IsPending, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Ellipse Fill="White" Height="15" Width="15" Canvas.Top="3" Canvas.Left="3"
/>
<Path StrokeThickness="2"
Classes.passed="{Binding Passed, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
<Classes.failed>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsRequired"/>
<Binding Path="!Passed"/>
</MultiBinding>
</Classes.failed>
<Classes.warning>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!IsRequired"/>
<Binding Path="!Passed"/>
</MultiBinding>
</Classes.warning>
</Path>
</Canvas>
<Arc StartAngle="280" SweepAngle="80" Margin="0 3 0 0" StrokeThickness="3"
Width="20" Height="20" VerticalAlignment="Top"
Classes.running="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
<Grid ColumnDefinitions="AUTO, AUTO" Margin="3">
<cc:StatusSpinner State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Label Grid.Column="1"
Content="{Binding PreCheckName, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.bold="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.bold="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}
Converter={StaticResource ResourceKey=IsStateConverter},
ConverterParameter=Running}"
/>
</Grid>
</UserControl>

View File

@ -1,4 +1,4 @@
using Avalonia;
using Avalonia;
using Avalonia.Controls;
namespace SPTInstaller.CustomControls;
@ -19,33 +19,6 @@ public partial class PreCheckItem : UserControl
public static readonly StyledProperty<string> PreCheckNameProperty =
AvaloniaProperty.Register<PreCheckItem, string>(nameof(PreCheckName));
public bool IsRunning
{
get => GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, value);
}
public static readonly StyledProperty<bool> IsRunningProperty =
AvaloniaProperty.Register<PreCheckItem, bool>(nameof(IsRunning));
public bool IsPending
{
get => GetValue(IsPendingProperty);
set => SetValue(IsPendingProperty, value);
}
public static readonly StyledProperty<bool> IsPendingProperty =
AvaloniaProperty.Register<PreCheckItem, bool>(nameof(IsPending));
public bool Passed
{
get => GetValue(PassedProperty);
set => SetValue(PassedProperty, value);
}
public static readonly StyledProperty<bool> PassedProperty =
AvaloniaProperty.Register<PreCheckItem, bool>(nameof(Passed));
public bool IsRequired
{
get => GetValue(IsRequiredProperty);
@ -54,4 +27,13 @@ public partial class PreCheckItem : UserControl
public static readonly StyledProperty<bool> IsRequiredProperty =
AvaloniaProperty.Register<PreCheckItem, bool>(nameof(IsRequired));
public StatusSpinner.SpinnerState State
{
get => GetValue(StateProperty);
set => SetValue(StateProperty, value);
}
public static readonly StyledProperty<StatusSpinner.SpinnerState> StateProperty =
AvaloniaProperty.Register<PreCheckItem, StatusSpinner.SpinnerState>(nameof(State));
}

View File

@ -0,0 +1,30 @@
using SPTInstaller.CustomControls;
using SPTInstaller.Models;
using System.Threading.Tasks;
namespace SPTInstaller.Installer_Tasks.PreChecks;
public class TestPreCheck : PreCheckBase
{
private StatusSpinner.SpinnerState _endState;
public static TestPreCheck FromRandomName(StatusSpinner.SpinnerState EndState) => new TestPreCheck($"{EndState} #{new Random().Next(0, 9999)}", EndState == StatusSpinner.SpinnerState.Error, EndState);
public TestPreCheck(string name, bool isRequired, StatusSpinner.SpinnerState endState) : base(name, isRequired)
{
_endState = endState;
}
public override async Task<PreCheckResult> CheckOperation()
{
await Task.Delay(1000);
switch (_endState)
{
case StatusSpinner.SpinnerState.Error:
return PreCheckResult.FromError("This is what a required precheck failing looks like");
case StatusSpinner.SpinnerState.Warning:
return PreCheckResult.FromError("This is what a non-required precheck failing looks like");
default:
return PreCheckResult.FromSuccess("This is what a successful precheck looks like");
}
}
}

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using SPTInstaller.CustomControls;
using System.Threading.Tasks;
namespace SPTInstaller.Interfaces;
@ -7,12 +8,8 @@ public interface IPreCheck
public string Id { get; }
public string Name { get; }
public bool IsRequired { get; }
public bool IsPending { get; set; }
public bool Passed { get; }
public string PreCheckDetails { get; }
public StatusSpinner.SpinnerState State { get; set; }
public Task<IResult> RunCheck();
}

View File

@ -1,4 +1,5 @@
using ReactiveUI;
using SPTInstaller.CustomControls;
using SPTInstaller.Interfaces;
using System.Threading.Tasks;
using System.Windows.Input;
@ -28,25 +29,11 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
set => this.RaiseAndSetIfChanged(ref _required, value);
}
private bool _passed;
public bool Passed
private StatusSpinner.SpinnerState _state;
public StatusSpinner.SpinnerState State
{
get => _passed;
set => this.RaiseAndSetIfChanged(ref _passed, value);
}
private bool _isPending;
public bool IsPending
{
get => _isPending;
set => this.RaiseAndSetIfChanged(ref _isPending, value);
}
private bool _isRunning;
public bool IsRunning
{
get => _isRunning;
set => this.RaiseAndSetIfChanged(ref _isRunning, value);
get => _state;
set => this.RaiseAndSetIfChanged(ref _state, value);
}
private string _preCheckDetails;
@ -89,12 +76,19 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
Id = Guid.NewGuid().ToString();
}
private StatusSpinner.SpinnerState ProcessResult(PreCheckResult result) =>
(result.Succeeded, IsRequired) switch
{
(true, _) => StatusSpinner.SpinnerState.OK,
(false, false) => StatusSpinner.SpinnerState.Warning,
(_, _) => StatusSpinner.SpinnerState.Error
};
public async Task<IResult> RunCheck()
{
IsRunning = true;
State = StatusSpinner.SpinnerState.Running;
var result = await CheckOperation();
Passed = result.Succeeded;
PreCheckDetails = !string.IsNullOrWhiteSpace(result.Message)
? result.Message
@ -104,10 +98,9 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
ActionButtonCommand = result.ButtonPressedCommand;
ActionButtonIsVisible = result.ActionButtonIsVisible;
IsRunning = false;
IsPending = false;
State = ProcessResult(result);
return Passed ? Result.FromSuccess() : Result.FromError($"PreCheck Failed: {Name}");
return State == StatusSpinner.SpinnerState.OK ? Result.FromSuccess() : Result.FromError($"PreCheck Failed: {Name}");
}
public abstract Task<PreCheckResult> CheckOperation();

View File

@ -4,6 +4,7 @@ using ReactiveUI;
using Serilog;
using Splat;
using SPTInstaller.Controllers;
using SPTInstaller.CustomControls;
using SPTInstaller.Helpers;
using SPTInstaller.Installer_Tasks;
using SPTInstaller.Installer_Tasks.PreChecks;
@ -31,10 +32,11 @@ internal class Program
// Register all the things
// Regestering as base classes so ReactiveUI works correctly. Doesn't seem to like the interfaces :(
ServiceHelper.Register<InternalData>();
#if !TEST
ServiceHelper.Register<PreCheckBase, NetFramework472PreCheck>();
ServiceHelper.Register<PreCheckBase, NetCore6PreCheck>();
#if !TEST
ServiceHelper.Register<PreCheckBase, FreeSpacePreCheck>();
var logPath = Path.Join(Environment.CurrentDirectory, "spt-aki-installer_.log");
@ -52,10 +54,14 @@ internal class Program
ServiceHelper.Register<InstallerTaskBase, CopyClientTask>();
ServiceHelper.Register<InstallerTaskBase, SetupClientTask>();
#else
for (int i = 0; i < 5; i++)
{
Locator.CurrentMutable.RegisterConstant<InstallerTaskBase>(TestTask.FromRandomName());
}
for (int i = 0; i < 5; i++)
{
Locator.CurrentMutable.RegisterConstant<InstallerTaskBase>(TestTask.FromRandomName());
}
Locator.CurrentMutable.RegisterConstant<PreCheckBase>(TestPreCheck.FromRandomName(StatusSpinner.SpinnerState.OK));
Locator.CurrentMutable.RegisterConstant<PreCheckBase>(TestPreCheck.FromRandomName(StatusSpinner.SpinnerState.Warning));
Locator.CurrentMutable.RegisterConstant<PreCheckBase>(TestPreCheck.FromRandomName(StatusSpinner.SpinnerState.Error));
#endif
// need the interfaces for the controller and splat won't resolve them since we need to base classes in avalonia (what a mess), so doing it manually here

View File

@ -39,14 +39,12 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<cc:DetailedPreCheckItem PreCheckName="{Binding Name}"
IsRunning="{Binding IsRunning}"
IsPending="{Binding IsPending}"
IsRequired="{Binding IsRequired}"
Passed="{Binding Passed}"
PreCheckDetails="{Binding PreCheckDetails}"
ActionButtonCommand="{Binding ActionButtonCommand}"
ActionButtonText="{Binding ActionButtonText}"
ActionButtonIsVisible="{Binding ActionButtonIsVisible}"
State="{Binding State}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>

View File

@ -36,10 +36,8 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<cc:PreCheckItem PreCheckName="{Binding Name}"
IsRunning="{Binding IsRunning}"
IsPending="{Binding IsPending}"
IsRequired="{Binding IsRequired}"
Passed="{Binding Passed}"
State="{Binding State}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>