feat(launcher): LU8 — logging in IS the character refresh; put Play above the fold
Two things the user hit while gating LU7. 1. "Refresh characters" disconnected the session they were playing. It opened a SECOND connection to the same account purely to read the roster, which the server treats as a new login — so refreshing while logged in kicked them out. It was also redundant the whole time: every ordinary login already carries the roster in the host's own status stream, and the orchestrator already folds it into the profiles (ApplyRosterLocked runs for play sessions, not just probes). Removed, along with "Add cached character", which existed to paper over a roster the launcher can now always obtain by itself. The account page says what happens instead: characters appear after you log in. ProbeAsync stays in Core — headless bots and the CLI use it, and it has its own tests. What is gone is offering it to a player as a button whose only effect they could observe was being disconnected. AnOrdinaryLoginFoldsTheReportedRosterIntoTheStore pins the replacement, including that it persists so the tree is still populated after a restart. 2. "There is no headless or gui option" under a selected character. The buttons were there — below the fold. The character page led with a plugins/login- commands form whose two 96px text boxes pushed the Launch card past the bottom of the scroll area, so the primary action was invisible unless you scrolled. Launch now comes first and the settings form sits under it. A player should never have to scroll to find Play. Full solution 14,374 passed, 0 failed under the release-gate filter. Not pushed — the user is testing locally first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2bff44a9fa
commit
18bbd37779
4 changed files with 101 additions and 101 deletions
|
|
@ -65,9 +65,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
SaveCharacterSettingsCommand = new RelayCommand(
|
||||
SaveCharacterSettings,
|
||||
() => IsCharacterSelected && CanInteract);
|
||||
RefreshCharactersCommand = new AsyncRelayCommand(
|
||||
RefreshCharactersAsync,
|
||||
() => CanProbe);
|
||||
LaunchGuiCommand = new AsyncRelayCommand(
|
||||
() => LaunchSelectedAsync(LaunchMode.Gui),
|
||||
() => CanLaunchGui);
|
||||
|
|
@ -114,7 +111,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
{
|
||||
if (SetProperty(ref _isBusy, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(CanProbe));
|
||||
OnPropertyChanged(nameof(CanLaunchGui));
|
||||
OnPropertyChanged(nameof(CanLaunchHeadless));
|
||||
OnPropertyChanged(nameof(CanLaunchAccountGuiSelect));
|
||||
|
|
@ -199,25 +195,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
public string LinuxGraphicalNotice =>
|
||||
_snapshot?.Platform.GraphicalLaunchDisabledReason ?? string.Empty;
|
||||
|
||||
public bool CanProbe =>
|
||||
CanInteract
|
||||
&& TryGetSelectedAccount(out string server, out string account)
|
||||
&& _orchestrator.GetProbeCapability(server, account).IsAvailable;
|
||||
|
||||
public string ProbeDisabledReason
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!TryGetSelectedAccount(out string server, out string account))
|
||||
{
|
||||
return "Select an account or one of its characters.";
|
||||
}
|
||||
|
||||
return _orchestrator.GetProbeCapability(server, account).Reason
|
||||
?? "Character refresh is available.";
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanLaunchGui => CanLaunch(LaunchMode.Gui);
|
||||
|
||||
public bool CanLaunchHeadless => CanLaunch(LaunchMode.Headless);
|
||||
|
|
@ -277,8 +254,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
|
||||
public RelayCommand SaveCharacterSettingsCommand { get; }
|
||||
|
||||
public AsyncRelayCommand RefreshCharactersCommand { get; }
|
||||
|
||||
public AsyncRelayCommand LaunchGuiCommand { get; }
|
||||
|
||||
public AsyncRelayCommand LaunchAccountGuiSelectCommand { get; }
|
||||
|
|
@ -365,7 +340,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
}
|
||||
|
||||
OnPropertyChanged(nameof(IsModalOpen));
|
||||
OnPropertyChanged(nameof(CanProbe));
|
||||
OnPropertyChanged(nameof(CanLaunchGui));
|
||||
OnPropertyChanged(nameof(CanLaunchHeadless));
|
||||
OnPropertyChanged(nameof(CanLaunchAccountGuiSelect));
|
||||
|
|
@ -422,8 +396,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
OnPropertyChanged(nameof(InstallationStatus));
|
||||
OnPropertyChanged(nameof(ShowLinuxGraphicalNotice));
|
||||
OnPropertyChanged(nameof(LinuxGraphicalNotice));
|
||||
OnPropertyChanged(nameof(CanProbe));
|
||||
OnPropertyChanged(nameof(ProbeDisabledReason));
|
||||
OnPropertyChanged(nameof(CanLaunchGui));
|
||||
OnPropertyChanged(nameof(CanLaunchHeadless));
|
||||
OnPropertyChanged(nameof(CanLaunchAccountGuiSelect));
|
||||
|
|
@ -461,8 +433,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
LoadCharacterDraft();
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(CanProbe));
|
||||
OnPropertyChanged(nameof(ProbeDisabledReason));
|
||||
OnPropertyChanged(nameof(CanLaunchGui));
|
||||
OnPropertyChanged(nameof(CanLaunchHeadless));
|
||||
OnPropertyChanged(nameof(CanLaunchAccountGuiSelect));
|
||||
|
|
@ -728,19 +698,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private Task RefreshCharactersAsync()
|
||||
{
|
||||
if (!TryGetSelectedAccount(out string serverName, out string accountName))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return RunOperationAsync(
|
||||
token => _orchestrator.ProbeAsync(serverName, accountName, token),
|
||||
$"Refreshing characters for {accountName}…",
|
||||
"Character refresh started. The roster will update from host status.");
|
||||
}
|
||||
|
||||
private Task LaunchSelectedAsync(LaunchMode mode)
|
||||
{
|
||||
LauncherCharacterSnapshot? character = GetSelectedCharacterSnapshot();
|
||||
|
|
@ -1049,7 +1006,6 @@ public sealed class LauncherWindowViewModel : ObservableObject, IDisposable
|
|||
EditSelectedCommand.NotifyCanExecuteChanged();
|
||||
RemoveSelectedCommand.NotifyCanExecuteChanged();
|
||||
SaveCharacterSettingsCommand.NotifyCanExecuteChanged();
|
||||
RefreshCharactersCommand.NotifyCanExecuteChanged();
|
||||
LaunchGuiCommand.NotifyCanExecuteChanged();
|
||||
LaunchAccountGuiSelectCommand.NotifyCanExecuteChanged();
|
||||
LaunchHeadlessCommand.NotifyCanExecuteChanged();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue