fix: manual my-sea draws persist on refresh + reloaded slots stay clickable — root cause was SeaDeal stamping slot.dataset.posKey w. selector form (".sea-pos-cover") while my-sea's inline _collectHandFromDom + template's _my_sea_slot.html use raw names ("cover"). Key mismatch silently dropped manual draws from the lock POST → server rejected empty hand → no row → refresh showed empty state. AUTO DRAW worked only because it assembled fullHand w. raw posNames directly, bypassing the broken collector. TDD — 2 new FTs pin the contract:
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful

- test_manual_draw_persists_on_refresh
- test_reloaded_slot_can_reopen_stage_modal_on_click

Changes:
- sea.js: stamp `dataset.posKey` w. raw name (strip `.sea-pos-` prefix); `_seaHand` keyed by raw; `_viewingPos` is raw too (`_hideStage` prefixes when querySelector'ing); new `SeaDeal.seedHand(handByPosName)` public method for init-time DOM-walk seeding.
- my_sea.html inline init: walk server-rendered filled slots, look up each card by `data-card-id` from the embedded deck JSON, reconstruct per-instance `reversed` + polarity from the slot's classes, hand the map to `SeaDeal.seedHand`. Without this, reloaded slots short-circuit the overlay click handler on `if (!_seaHand[pos]) return;`.

The gameroom-side SeaDeal callers in `_sea_overlay.html` continue to pass selector form (SeaDeal accepts either — `_posName` helper strips prefix tolerantly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-20 15:08:49 -04:00
parent bb44aa326a
commit 97a6da28a5
3 changed files with 147 additions and 9 deletions

View File

@@ -796,6 +796,50 @@
_lockSpread();
}
// Seed SeaDeal's `_seaHand` from the server-rendered
// saved slots so they remain clickable for re-opening
// the stage modal after a refresh. Walks the filled
// slots + maps each `data-card-id` against the embedded
// deck JSON. Without this, the overlay click handler
// short-circuits on `if (!_seaHand[pos]) return;` —
// sea.js's in-memory `_seaHand` only gets populated by
// openStage/register during the live session.
// (User-reported 2026-05-21.)
if (window.SeaDeal && window.SeaDeal.seedHand) {
var _allCards = (_deckData.levity || [])
.concat(_deckData.gravity || []);
var _byId = {};
_allCards.forEach(function (c) { _byId[c.id] = c; });
var _seed = {};
cross.querySelectorAll(
'.sea-card-slot.sea-card-slot--filled'
).forEach(function (slot) {
var posName = slot.dataset.posKey;
var cardId = parseInt(slot.dataset.cardId, 10);
var card = _byId[cardId];
if (!posName || !card) return;
// Reconstruct the per-instance `reversed` flag
// from the slot's DOM class (the deck JSON's
// `reversed` field is the original-shuffle axis
// — server's saved hand may have flipped it
// independently). Polarity also DOM-sourced.
var slotCard = {};
for (var k in card) {
if (Object.prototype.hasOwnProperty.call(card, k)) {
slotCard[k] = card[k];
}
}
slotCard.reversed = slot.classList.contains(
'sea-card-slot--reversed'
);
var isLevity = slot.classList.contains(
'sea-card-slot--levity'
);
_seed[posName] = { card: slotCard, isLevity: isLevity };
});
SeaDeal.seedHand(_seed);
}
// Belt-and-braces autofill defense (paired w. autocomplete=
// off on the hidden input above). Firefox occasionally
// restores form-history values on soft reload even on