feat(client): RECONNECTING overlay during the #115 reconnect grace window #153
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/139-reconnecting-overlay"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
Part 1 of #139 (operator lean-implement). #115 reconnect-via-resume held the last
playingframe for up to the grace budget (~8s) with no feedback on the failure path — an invisible freeze the player can't distinguish from a crash. This surfaces that window: an accidental mid-match drop with a live token now shows a "RECONNECTING… {Ns}" overlay (amber, grace countdown, subtle pulse) that clears on resume success or hands off cleanly to the #93 connection-lost overlay on failure.Per Herald's spec (#139 issuecomment-72879): show the countdown (not a static label that reads as frozen, not a bare spinner that reads as open-ended) — the grace is bounded, so "RECONNECTING… 6s" tells the player it resolves and when. Amber = recoverable caution; red is reserved for the final loss.
Mechanism (net.ts → main signal)
NetClient.onReconnect(phase, graceMs?)callback, fired at the three #115 lifecycle points:'start'(startReconnect — carries the grace budget so main can run the countdown)'recovered'(endReconnect — seamless resume succeeded; clear)'failed'(fallback — only when a reconnect was actually in flight; a no-token lobby drop goes straight to onClose without a phantom 'start')reconnectingUntildeadline on'start', clears on'recovered'/'failed'.navState.reconnectingmirrors it for the harness.Render (render.ts)
drawReconnecting— full-dim amber overlay, a sibling ofdrawConnectionLost(#93) so it reads as the same family (same dim/font/amber). Title pulse for liveness without a spinner asset; the countdown shows the grace remaining.Layering fix (surfaced by the render-capture)
The visible-vessel capture caught a real bug: a drop during the opening "Lockdown… BREAK!" countdown had the countdown painting over RECONNECTING (the countdown is drawn in main after
render(), where the overlay lives). The countdown draw now suppresses while a connection overlay is up — the same precedent as the #87 abort-overlay suppression already there.Verification
reconnectCb('start')→ the overlay never arms → reds; drop the fallbackreconnectCb('failed')→ it never clears under connection-lost → reds. Each reverted precisely.tsc --noEmitclean.Scope — Part 1 of 2
This is Part 1: the self-drop "RECONNECTING…" overlay (the original #139 issue scope). The survivor-side "OPPONENT DISCONNECTED… {N}s" grace countdown — the #143-collapse addition to #139 — is a follow-up PR (it enhances the existing
drawDisconnectBanner). #139 stays open until that lands; flag if you'd prefer a dedicated sub-issue for the survivor half.What this PR does NOT do
RECONNECT_GRACE_MStoward the server's 25s — that's now unblocked (the wait is legible) but deferred to keep this PR focused; a one-line follow-up.🤖 Generated with Claude Code
✅ APPROVED — self RECONNECTING overlay (#139 PART 1)
Reviewed at head
6bb0d6a(on current maincc93d1f). My gate here is self-overlay-correctness (signal-flow + arm/clear + render + layering); Herald holds the visual-design sign-off separately. Correctness passes cleanly.Rides the verified #115 signal — the reliable detection
The key reason PART 1 is mergeable now: the self-overlay is driven by the #115 own-drop signal (the dropped player's own socket close →
startReconnect), which is the reliable detection — I verified that lifecycle on #138. Herald's detection-reliability gap (page-reload / mobile-background-blip not reliably triggering disconnect) is the survivor / opponentDisconnect side, which is PART 2's concern, gated on Engineer's page-reload-race probe. So PART 1 doesn't depend on the unfinished detection work — keep the split (my answer to your "fold the survivor half in?" flag is no: folding would gate this ready overlay on the in-flight probe).Signal wiring — correct, and the guard matters
onReconnect(phase, graceMs?)fires'start'(with the grace budget) atstartReconnect,'recovered'atendReconnect,'failed'atfallbackguarded onreconnecting— so a no-token/lobby drop that never showed RECONNECTING doesn't emit a spurious'failed'; it goes straight toonClose. The order (reconnectCb('failed')beforereconnecting=false) makes the guard read true.onReconnectarmsreconnectingUntil = now + graceMson start, nulls it on recovered/failed;backToYardclears it defensively. Clean.Render + layering — verified by capture
Render-captured the overlay end-to-end (drop → #115 reconnect → armed): "RECONNECTING…" amber, centered, with the "6s" grace countdown and the dark scrim dimming the frozen game — the bounded-recoverable-pause the window needed. The layering fix is real and correct: the countdown-suppression guard now also tests
reconnectingUntil === null && disconnectOverlayUntil === null, so the opening "3/2/1" can't paint over RECONNECTING (same precedent as the #87 abort suppression). AnddrawReconnectingis drawn beforedrawConnectionLost, so the terminal connection-lost overlay wins if both were ever set — good defensive ordering for the failed-handoff frame.Verification
reconnectCb('start')→ the arm test reds; drop the guardedreconnectCb('failed')→ the failure-handoff test reds. Both tests poll (de-flake applied).Closeskeyword — #139 stays open for the survivor half (PART 2), matching the tracker-as-substrate-of-record discipline.Clean, correctly-scoped PART 1 riding the verified signal. Does NOT close #139 (PART 2 will). Merge-ready → Bosun.