launcher: welcome screen + guided setup wizard

First launch went straight to a Host/Join card picker with no
framing. Replaced with a two-phase onboarding:

WelcomeView — branded intro with the wordmark, three feature
bullets (2 min setup, free, works with regular Rec Room), and one
big "Get started" button. Dismissed via AppState.WelcomeSeen.

SetupView — multi-step wizard with a STEP X OF N indicator:
1. Host vs Join card picker (self-advances on click)
2. Rec Room install (shows auto-detected path with a green "we
   found it" banner; Pick... overrides)
3. (Host only) Server name + WHO CAN JOIN radios
4. (Host only) Photon AppIds — "Open setup walkthrough" launches
   the existing PhotonWizard, or paste fields below for advanced
   users; live status line confirms when AppIds are set
5. Done card with Finish button

Join mode hops 2 -> done since it doesn't need server config or
Photon. AppState.SetupComplete gates entry; future launches skip
the wizard and route directly to HostView / JoinView. Existing
state files get auto-migrated on load (Mode set + RecRoomPath
populated = WelcomeSeen + SetupComplete backfilled to true, then
flushed). Settings -> "Re-run setup" clears the flag for users
who want to walk through again.
This commit is contained in:
Alexa
2026-05-27 11:25:28 +03:00
parent 5b98886937
commit 5452ff06ee
3 changed files with 511 additions and 86 deletions
+33 -1
View File
@@ -52,6 +52,19 @@ public sealed class AppState
[JsonPropertyName("serverName")]
public string ServerName { get; set; } = string.Empty;
/// <summary>True once the user has dismissed the branded welcome
/// screen. Subsequent launches skip straight to the setup wizard
/// (or the main host/join view if setup is also done).</summary>
[JsonPropertyName("welcomeSeen")]
public bool WelcomeSeen { get; set; }
/// <summary>True once the user has finished the multi-step setup
/// wizard. Future launches skip the wizard and open the
/// host/join view directly. Settings -> "Re-run setup" can clear
/// this if the user wants to walk through again.</summary>
[JsonPropertyName("setupComplete")]
public bool SetupComplete { get; set; }
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
@@ -65,7 +78,26 @@ public sealed class AppState
try
{
var json = File.ReadAllText(AppPaths.StateFile);
return JsonSerializer.Deserialize<AppState>(json, JsonOptions) ?? new AppState();
var state = JsonSerializer.Deserialize<AppState>(json, JsonOptions) ?? new AppState();
// Backfill the welcome/setup flags for users upgrading from a
// pre-wizard build. If they already had a working host setup
// they've effectively passed those screens — don't drag them
// back through the wizard on first launch of the new build.
var migrated = false;
if (!state.WelcomeSeen && state.Mode != AppMode.Unset)
{ state.WelcomeSeen = true; migrated = true; }
if (!state.SetupComplete && state.Mode != AppMode.Unset &&
!string.IsNullOrEmpty(state.RecRoomPath) &&
(state.Mode == AppMode.Join || !string.IsNullOrEmpty(state.PhotonAppId)))
{ state.SetupComplete = true; migrated = true; }
// Flush the migration so subsequent launches read the
// backfilled values directly from disk — keeps the file as
// the authoritative source instead of needing to re-derive
// them every load.
if (migrated) { try { state.Save(); } catch { } }
return state;
}
catch
{