feat(launcher): LU4 — first-run setup ends with "Setup complete" and an OK button
Setup used to finish by leaving a full progress bar and a status line on screen, with the same Validate / Cancel bake / Close / Build and install row underneath. Nothing said "you are done" and nothing said what to press. The wizard now swaps its whole form for a plain completion panel: "Setup complete", one sentence saying the content was built and verified, and a single OK that closes the dialog and returns to the launcher. Raised at exactly one point — after _onInstalled publishes the record — so the launcher behind the dialog is already in its launch-enabled state when OK is pressed, and the "Client setup required" banner is gone the moment the user gets back. The cancelled and failed branches deliberately never reach it and keep their existing status/error reporting. Tests: FirstRunSetupEndsWithACompletionPanelThatOkReturnsFrom (form hidden, panel shown, record published before OK, wizard reopens as an ordinary form afterwards) and AFailedFirstRunSetupNeverShowsTheCompletionPanel. Launcher 61 passed. Campaign LU slice LU4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a01ff42640
commit
0a2defb618
3 changed files with 138 additions and 0 deletions
|
|
@ -350,6 +350,25 @@
|
|||
VerticalAlignment="Center">
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="12">
|
||||
<!-- LU4: the completion panel replaces the form outright, so the
|
||||
user is not left staring at a finished progress bar wondering
|
||||
whether they are allowed to close the window. -->
|
||||
<StackPanel Spacing="16" IsVisible="{Binding FirstRunWizardShell.IsCompleted}">
|
||||
<TextBlock Text="{Binding FirstRunWizardShell.CompletedTitle}"
|
||||
FontSize="24"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding FirstRunWizardShell.CompletedBody}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button Content="OK"
|
||||
Classes="primary"
|
||||
IsDefault="True"
|
||||
HorizontalAlignment="Right"
|
||||
MinWidth="96"
|
||||
AutomationProperties.Name="Finish first-run setup"
|
||||
Command="{Binding FirstRunWizardShell.AcknowledgeCompletionCommand}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="12" IsVisible="{Binding FirstRunWizardShell.ShowSetupForm}">
|
||||
<TextBlock Text="{Binding FirstRunWizardShell.Title}" FontSize="24" FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding FirstRunWizardShell.Body}" TextWrapping="Wrap" />
|
||||
|
||||
|
|
@ -423,6 +442,7 @@
|
|||
AutomationProperties.Name="Build and install client content"
|
||||
Command="{Binding FirstRunWizardShell.StartCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
private string? _error;
|
||||
private LauncherInstallPhase _phase = LauncherInstallPhase.Idle;
|
||||
private double _progressPercent;
|
||||
private bool _isCompleted;
|
||||
private bool _isOpen;
|
||||
private bool _isRunning;
|
||||
private bool _isDatDirectoryValid;
|
||||
|
|
@ -44,6 +45,9 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
_canStart = canStart ?? (() => true);
|
||||
|
||||
OpenCommand = new RelayCommand(Open, () => !_disposed && _canOpen());
|
||||
AcknowledgeCompletionCommand = new RelayCommand(
|
||||
AcknowledgeCompletion,
|
||||
() => IsCompleted && !IsRunning);
|
||||
CloseCommand = new RelayCommand(Close, () => !IsRunning);
|
||||
ValidateCommand = new RelayCommand(Validate, () => !IsRunning);
|
||||
StartCommand = new AsyncRelayCommand(StartAsync, CanBeginInstall);
|
||||
|
|
@ -183,6 +187,9 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
|
||||
public RelayCommand OpenCommand { get; }
|
||||
|
||||
/// <summary>LU4: OK on the "Setup complete" panel.</summary>
|
||||
public RelayCommand AcknowledgeCompletionCommand { get; }
|
||||
|
||||
public RelayCommand CloseCommand { get; }
|
||||
|
||||
public RelayCommand ValidateCommand { get; }
|
||||
|
|
@ -200,6 +207,34 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
: message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LU4: true once first-run setup has actually succeeded — the pak was
|
||||
/// built, verified, and its record published. The wizard replaces its form
|
||||
/// with a plain "Setup complete" panel whose single OK button returns to
|
||||
/// the launcher, instead of leaving the user looking at a finished
|
||||
/// progress bar wondering whether they may close the window.
|
||||
/// </summary>
|
||||
public bool IsCompleted
|
||||
{
|
||||
get => _isCompleted;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _isCompleted, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(ShowSetupForm));
|
||||
NotifyCommandStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The form and the completion panel are mutually exclusive.</summary>
|
||||
public bool ShowSetupForm => !IsCompleted;
|
||||
|
||||
public string CompletedTitle => "Setup complete";
|
||||
|
||||
public string CompletedBody =>
|
||||
"acdream built and verified your game content. You can play now.";
|
||||
|
||||
public void NotifyCommandStates()
|
||||
{
|
||||
OpenCommand.NotifyCanExecuteChanged();
|
||||
|
|
@ -207,6 +242,7 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
ValidateCommand.NotifyCanExecuteChanged();
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
CancelCommand.NotifyCanExecuteChanged();
|
||||
AcknowledgeCompletionCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
|
|
@ -231,6 +267,15 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
NotifyCommandStates();
|
||||
}
|
||||
|
||||
/// <summary>LU4: OK on the completion panel. Closes the wizard and leaves
|
||||
/// it ready to be reopened as an ordinary form (a user may reinstall
|
||||
/// content later against a different DAT folder).</summary>
|
||||
private void AcknowledgeCompletion()
|
||||
{
|
||||
IsCompleted = false;
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
private void Open()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DatDirectory))
|
||||
|
|
@ -306,6 +351,11 @@ public sealed class FirstRunInstallerViewModel : ObservableObject, IDisposable
|
|||
Phase = LauncherInstallPhase.Completed;
|
||||
ProgressPercent = 100;
|
||||
Status = "Client content installed and verified. Launch is enabled.";
|
||||
// LU4: raised only here, AFTER the record is published, so the
|
||||
// launcher behind the dialog is already in its launch-enabled
|
||||
// state when the user presses OK. The cancelled and failed
|
||||
// branches below deliberately never reach this.
|
||||
IsCompleted = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -442,6 +442,74 @@ public sealed class LauncherWindowViewModelTests
|
|||
Assert.True(orchestrator.ClearCalled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LU4. A successful first-run setup ends with a plain "Setup complete"
|
||||
/// panel and one OK button that returns to the launcher — not a finished
|
||||
/// progress bar the user has to decide what to do with.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task FirstRunSetupEndsWithACompletionPanelThatOkReturnsFrom()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator
|
||||
{
|
||||
Session = FakeLauncherOrchestrator.CreateSession(
|
||||
LauncherActivityState.Exited,
|
||||
"Exited cleanly."),
|
||||
};
|
||||
var installer = new FakeLauncherInstaller();
|
||||
using var viewModel = CreateInitialized(orchestrator, installer);
|
||||
|
||||
viewModel.FirstRunWizardShell.OpenCommand.Execute(null);
|
||||
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
|
||||
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
|
||||
|
||||
await viewModel.FirstRunWizardShell.StartCommand.ExecuteAsync();
|
||||
|
||||
Assert.True(viewModel.FirstRunWizardShell.IsCompleted);
|
||||
// The form is gone; the completion panel is what the user sees.
|
||||
Assert.False(viewModel.FirstRunWizardShell.ShowSetupForm);
|
||||
Assert.True(viewModel.FirstRunWizardShell.IsOpen);
|
||||
// The record is already published, so the launcher behind the dialog
|
||||
// is in its launch-enabled state before OK is pressed.
|
||||
Assert.NotNull(orchestrator.InstalledRecord);
|
||||
|
||||
viewModel.FirstRunWizardShell.AcknowledgeCompletionCommand.Execute(null);
|
||||
|
||||
Assert.False(viewModel.FirstRunWizardShell.IsOpen);
|
||||
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
|
||||
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LU4. Cancellation and failure must NOT claim success. They keep their
|
||||
/// existing status/error reporting and leave the form on screen.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task AFailedFirstRunSetupNeverShowsTheCompletionPanel()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator
|
||||
{
|
||||
Session = FakeLauncherOrchestrator.CreateSession(
|
||||
LauncherActivityState.Exited,
|
||||
"Exited cleanly."),
|
||||
};
|
||||
var installer = new FakeLauncherInstaller
|
||||
{
|
||||
InstallHandler = (_, _, _, _) =>
|
||||
Task.FromException<LauncherInstallResult>(
|
||||
new LauncherInstallException("The bake tool exited with code 3.")),
|
||||
};
|
||||
using var viewModel = CreateInitialized(orchestrator, installer);
|
||||
|
||||
viewModel.FirstRunWizardShell.OpenCommand.Execute(null);
|
||||
await viewModel.FirstRunWizardShell.StartCommand.ExecuteAsync();
|
||||
|
||||
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
|
||||
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
|
||||
Assert.True(viewModel.FirstRunWizardShell.HasError);
|
||||
Assert.Null(orchestrator.InstalledRecord);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LU1. Ordinary startup trusts a remembered digest so the window is not
|
||||
/// held behind a multi-second hash of a ~28 GiB file; "Verify files" is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue