fix(auth): close two regressions from the resume refactor
Critical: play-page bootstrap infinite loop when AUTH_ENABLED and no resume snapshot. The refactor changed the gate from `if (AUTH_ENABLED && hasSnapshot)` to `if (AUTH_ENABLED)`, so any snapshot-less /play entry (the common case — normal card/preset/custom start) entered the async branch, got null from consumeResumeSnapshot, bumped retryBootstrap, and re-ran the effect forever. Restored the peek-before-await: only enter the async resume branch when a snapshot actually exists; otherwise fall straight through to normal bootstrap. Verified via control-flow simulation across all three paths (no snapshot / snapshot + signed in / snapshot + not signed in). Major: homepage auto-started a game after a bare OAuth login. Routing persistPendingStart through AuthModal.onBeforeOAuth fired it for every OAuth redirect, including bare logins via UserChip / StyleModal onRequireAuth (where pendingAction is null and the user only wanted to sign in). Guarded the snapshot on `pendingAction === "start"` so only the mid-start flow persists; bare logins no longer resurrect the form and auto-start on return.
This commit is contained in:
+13
-5
@@ -1478,16 +1478,24 @@ function PlayInner() {
|
||||
// re-bootstrapping from `?card=…` (which would restart the story). OTP
|
||||
// login never writes a snapshot — its onSuccess retry keeps state
|
||||
// in-memory.
|
||||
if (AUTH_ENABLED) {
|
||||
// Let the async resume run; on failure (no snapshot / not signed in /
|
||||
// corrupt) it relinquishes the slot so the normal bootstrap below
|
||||
// re-runs. Either way return here — the sync body must not run while the
|
||||
// OAuth return is being reconciled.
|
||||
//
|
||||
// Peek before awaiting: when there's no snapshot (the common case —
|
||||
// normal card/preset/custom entry), fall straight through to the
|
||||
// bootstrap below. Only when a snapshot exists do we enter the async
|
||||
// gate, which itself removes the entry. This keeps the no-snapshot path
|
||||
// off the retryBootstrap re-trigger loop entirely.
|
||||
if (
|
||||
AUTH_ENABLED &&
|
||||
sessionStorage.getItem(PLAY_RESUME_KEY) !== null
|
||||
) {
|
||||
void (async () => {
|
||||
const snap = await consumeResumeSnapshot<PlayResumeSnapshot>(
|
||||
PLAY_RESUME_KEY,
|
||||
);
|
||||
if (!snap) {
|
||||
// Snapshot existed but user isn't signed in / payload corrupt →
|
||||
// consumeResumeSnapshot already removed it. Relinquish the slot so
|
||||
// the normal bootstrap below re-runs on the next effect cycle.
|
||||
startedRef.current = false;
|
||||
setRetryBootstrap((n) => n + 1);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user