From f78177778f5bdedb10ddfe27e3edd1c4d693f26e Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Sun, 3 May 2026 22:11:07 -0400 Subject: [PATCH] =?UTF-8?q?SIG=20SELECT=20exit:=202s=20hang=20after=20tray?= =?UTF-8?q?=20closes;=20suppress=20waiting=20msg=20if=20PICK=20SKY=20alrea?= =?UTF-8?q?dy=20up=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - polarity_room_done handler in sig-select.js wraps Tray.placeSig's callback in setTimeout(_settle, 2000) so the user gets a beat to register the tray closing before the overlay vanishes. Visual order now: stage → tray slides in → sig fades into the tray cell → tray slides out → 2s pause → overlay dismisses → table hex. - _showWaitingMsg early-exits if #id_pick_sky_btn is already revealed. The cross-polarity case — the OTHER room finishes WHILE this gamer's tray sequence is mid-flight — fires pick_sky_available during the hang, which removes any waiting msg & shows PICK SKY. When _settle fires after the hang, the PICK SKY check skips the now-stale waiting msg. - 2 SigSelectSpec specs: 2s delay before overlay dismisses; waiting msg suppressed when pick_sky_btn is visible. jasmine.clock() drives the setTimeout in both. Code architected by Disco DeDisco Git commit message Co-Authored-By: Claude Opus 4.7 (1M context) --- src/apps/epic/static/apps/epic/sig-select.js | 19 ++++++++--- src/static/tests/SigSelectSpec.js | 36 +++++++++++++++++--- src/static_src/tests/SigSelectSpec.js | 36 +++++++++++++++++--- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/apps/epic/static/apps/epic/sig-select.js b/src/apps/epic/static/apps/epic/sig-select.js index 4109582..2d5490d 100644 --- a/src/apps/epic/static/apps/epic/sig-select.js +++ b/src/apps/epic/static/apps/epic/sig-select.js @@ -494,6 +494,11 @@ var SigSelect = (function () { function _showWaitingMsg(pendingPolarity) { if (document.getElementById('id_hex_waiting_msg')) return; + // If the OTHER polarity finished before our tray sequence completed, + // pick_sky_available will already have fired and revealed PICK SKY. + // In that case skip the waiting msg so the two don't co-exist. + var pickSkyBtn = document.getElementById('id_pick_sky_btn'); + if (pickSkyBtn && pickSkyBtn.style.display !== 'none') return; var msg = document.createElement('p'); msg.id = 'id_hex_waiting_msg'; msg.textContent = pendingPolarity === 'gravity' @@ -529,16 +534,20 @@ var SigSelect = (function () { if (!overlay) return; if (e.detail.polarity !== userPolarity) return; var pendingPolarity = userPolarity === 'levity' ? 'gravity' : 'levity'; - // Tray-place sequence first (visually anchors the sig stage's exit); - // overlay dismissal + waiting msg run on Tray.placeSig's completion - // callback so the user sees: stage card → tray slides in → sig fades - // into the tray cell → tray slides out → table hex w. waiting msg. + // Tray-place sequence first (visually anchors the sig stage's exit), + // then a 2s hang so the user can register the tray closing before + // the overlay vanishes; overlay dismissal + waiting msg run last. + // User sees: stage card → tray slides in → sig fades into the tray + // cell → tray slides out → 2s pause → overlay dismisses → table hex + // w. waiting msg (or PICK SKY btn if both polarities are done). function _settle() { _dismissSigOverlay(); _showWaitingMsg(pendingPolarity); } if (typeof Tray !== "undefined" && Tray.placeSig) { - Tray.placeSig(stageCard, _settle); + Tray.placeSig(stageCard, function () { + setTimeout(_settle, 2000); + }); } else { _settle(); } diff --git a/src/static/tests/SigSelectSpec.js b/src/static/tests/SigSelectSpec.js index cf3da29..a5ae3ff 100644 --- a/src/static/tests/SigSelectSpec.js +++ b/src/static/tests/SigSelectSpec.js @@ -825,8 +825,7 @@ describe("SigSelect", () => { describe("polarity_room_done → tray sequence", () => { beforeEach(() => { - // .table-center is appended to by _showWaitingMsg in the dismiss - // path; provide one so dismissal doesn't error. + jasmine.clock().install(); const center = document.createElement("div"); center.className = "table-center"; document.body.appendChild(center); @@ -835,7 +834,8 @@ describe("SigSelect", () => { }); afterEach(() => { - document.querySelectorAll(".table-center, #id_hex_waiting_msg") + jasmine.clock().uninstall(); + document.querySelectorAll(".table-center, #id_hex_waiting_msg, #id_pick_sky_btn") .forEach((el) => el.remove()); }); @@ -855,17 +855,43 @@ describe("SigSelect", () => { expect(Tray.placeSig).not.toHaveBeenCalled(); }); - it("dismisses the overlay only AFTER Tray.placeSig's callback fires", () => { + it("dismisses the overlay 2s after Tray.placeSig's callback fires", () => { window.dispatchEvent(new CustomEvent("room:polarity_room_done", { detail: { polarity: "levity" }, })); - // Overlay still mounted — dismissal deferred to tray callback. + // Overlay still mounted — dismissal deferred to tray callback + hang. expect(document.querySelector(".sig-overlay")).not.toBe(null); const cb = Tray.placeSig.calls.mostRecent().args[1]; cb(); + // 1.999s after callback — overlay still up. + jasmine.clock().tick(1999); + expect(document.querySelector(".sig-overlay")).not.toBe(null); + // At 2s — overlay dismissed; waiting msg added. + jasmine.clock().tick(1); expect(document.querySelector(".sig-overlay")).toBe(null); expect(document.getElementById("id_hex_waiting_msg")).not.toBe(null); }); + + it("does NOT add the waiting msg when pick_sky_btn is already revealed", () => { + // pick_sky_available may fire DURING the tray sequence (other + // polarity finishes first). When the tray callback then hangs + + // dismisses, _settle must check whether PICK SKY is up and skip + // the "Levity appraising…" / "Gravity settling…" message so it + // doesn't co-exist w. the btn. + const btn = document.createElement("button"); + btn.id = "id_pick_sky_btn"; + btn.style.display = ""; // visible — pick_sky_available fired + document.body.appendChild(btn); + + window.dispatchEvent(new CustomEvent("room:polarity_room_done", { + detail: { polarity: "levity" }, + })); + const cb = Tray.placeSig.calls.mostRecent().args[1]; + cb(); + jasmine.clock().tick(2001); + expect(document.querySelector(".sig-overlay")).toBe(null); + expect(document.getElementById("id_hex_waiting_msg")).toBe(null); + }); }); }); diff --git a/src/static_src/tests/SigSelectSpec.js b/src/static_src/tests/SigSelectSpec.js index cf3da29..a5ae3ff 100644 --- a/src/static_src/tests/SigSelectSpec.js +++ b/src/static_src/tests/SigSelectSpec.js @@ -825,8 +825,7 @@ describe("SigSelect", () => { describe("polarity_room_done → tray sequence", () => { beforeEach(() => { - // .table-center is appended to by _showWaitingMsg in the dismiss - // path; provide one so dismissal doesn't error. + jasmine.clock().install(); const center = document.createElement("div"); center.className = "table-center"; document.body.appendChild(center); @@ -835,7 +834,8 @@ describe("SigSelect", () => { }); afterEach(() => { - document.querySelectorAll(".table-center, #id_hex_waiting_msg") + jasmine.clock().uninstall(); + document.querySelectorAll(".table-center, #id_hex_waiting_msg, #id_pick_sky_btn") .forEach((el) => el.remove()); }); @@ -855,17 +855,43 @@ describe("SigSelect", () => { expect(Tray.placeSig).not.toHaveBeenCalled(); }); - it("dismisses the overlay only AFTER Tray.placeSig's callback fires", () => { + it("dismisses the overlay 2s after Tray.placeSig's callback fires", () => { window.dispatchEvent(new CustomEvent("room:polarity_room_done", { detail: { polarity: "levity" }, })); - // Overlay still mounted — dismissal deferred to tray callback. + // Overlay still mounted — dismissal deferred to tray callback + hang. expect(document.querySelector(".sig-overlay")).not.toBe(null); const cb = Tray.placeSig.calls.mostRecent().args[1]; cb(); + // 1.999s after callback — overlay still up. + jasmine.clock().tick(1999); + expect(document.querySelector(".sig-overlay")).not.toBe(null); + // At 2s — overlay dismissed; waiting msg added. + jasmine.clock().tick(1); expect(document.querySelector(".sig-overlay")).toBe(null); expect(document.getElementById("id_hex_waiting_msg")).not.toBe(null); }); + + it("does NOT add the waiting msg when pick_sky_btn is already revealed", () => { + // pick_sky_available may fire DURING the tray sequence (other + // polarity finishes first). When the tray callback then hangs + + // dismisses, _settle must check whether PICK SKY is up and skip + // the "Levity appraising…" / "Gravity settling…" message so it + // doesn't co-exist w. the btn. + const btn = document.createElement("button"); + btn.id = "id_pick_sky_btn"; + btn.style.display = ""; // visible — pick_sky_available fired + document.body.appendChild(btn); + + window.dispatchEvent(new CustomEvent("room:polarity_room_done", { + detail: { polarity: "levity" }, + })); + const cb = Tray.placeSig.calls.mostRecent().args[1]; + cb(); + jasmine.clock().tick(2001); + expect(document.querySelector(".sig-overlay")).toBe(null); + expect(document.getElementById("id_hex_waiting_msg")).toBe(null); + }); }); });