Sig Select gate-view: CONT GAME/NVM keep the acting seat; restore the live countdown numeral on load — TDD
Two Sig-Select seat-switch follow-ups from the 2026-06-05 countdown sprint.
(1) CONT GAME + gear NVM pos-1 shuttle.
The GATE VIEW (room_gate.html) CONT GAME button and the gear-menu NVM both
linked to epic:room with NO ?seat, so a CARTE multi-seat gamer acting at pos 4
got shuttled back to owned[0] (pos 1) instead of staying on his acting seat —
the same gap bedc489 fixed for the GATE VIEW nav buttons. room_gate now
computes a single table_url (epic:room + ?seat=<current_slot> when seated) and
feeds it to both the CONT GAME onclick and the NVM include. The table view
already reads ?seat, so the gamer lands on the seat he was viewing. Added
`reverse` import to epic/views.py. 2 ITs in CarteTrayFollowsSelectedSeatTest
(cont-game + nvm carry the acting seat; default targets lowest owned); updated
RoomGateViewTest.test_nvm_returns_to_room_hex to expect the seat-carrying href.
(2) Live countdown numeral not restored on a fresh seat view mid-countdown.
countdown_start is a ONE-SHOT WS broadcast: a gamer (esp. a CARTE owner
switching to an already-ready seat) who loads the view after it fired saw a
static WAIT NVM, never the 12s flashing numeral — the redirect still fired, so
it was a visual-restore-on-load gap. The cache entry now stores the absolute
deadline alongside the timer token ({token, deadline} dict, was a bare token
string); _fire's token guard reads either shape defensively so a stale string
from an older deploy can't crash the callback. New tasks.countdown_remaining()
derives the seconds-left from the deadline (None when no countdown / elapsed).
The room view seeds ctx["countdown_remaining"] for the acting polarity;
_sig_select_overlay.html carries data-countdown-remaining; sig-select.js's
_replayReservations restores _showCountdown(remaining) on load when a count is
live, else falls back to WAIT NVM. 4 unit tests (CountdownRemainingTest), 2 ITs
(SigSelectRenderingTest), 3 Jasmine specs (SigSelectSpec countdown-restore).
922 epic+gameboard ITs + 19 task UTs + Jasmine SpecRunner all green.
Trap [[feedback-ws-cursor-group-must-match-acting-seat]].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse, HttpResponseForbidden, JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
|
||||
@@ -610,8 +611,15 @@ def _role_select_context(room, user, seat_param=None):
|
||||
str(res.card_id): res.role
|
||||
for res in room.sig_reservations.filter(polarity=polarity_const)
|
||||
}
|
||||
# Seconds left on a live polarity countdown, so a gamer landing on
|
||||
# this seat view mid-countdown restores the flashing numeral instead
|
||||
# of a static WAIT NVM (countdown_start is a one-shot WS broadcast).
|
||||
# None when no countdown is running → template renders 0.
|
||||
from apps.epic.tasks import countdown_remaining
|
||||
ctx["countdown_remaining"] = countdown_remaining(room.id, polarity_const)
|
||||
else:
|
||||
reservations = {}
|
||||
ctx["countdown_remaining"] = None
|
||||
ctx["sig_reservations_json"] = json.dumps(reservations)
|
||||
|
||||
if user_polarity == 'levity':
|
||||
@@ -789,8 +797,16 @@ def room_gate(request, room_id):
|
||||
# partial's slot conditionals read. The renewal modal's own keys override
|
||||
# below.
|
||||
ctx = _gate_context(room, request.user, request.GET.get("seat"))
|
||||
# CONT GAME + the gear NVM both return to the table hex — but they must land
|
||||
# the gamer on his ACTING seat (the ?seat-selected pos-circle), not owned[0].
|
||||
# Without the ?seat a CARTE multi-seat gamer acting at pos 4 got shuttled
|
||||
# back to pos 1. Mirrors the GATE VIEW nav buttons (bedc489).
|
||||
table_url = reverse("epic:room", args=[room.id])
|
||||
if ctx.get("current_slot"):
|
||||
table_url += f"?seat={ctx['current_slot']}"
|
||||
ctx.update({
|
||||
"room": room,
|
||||
"table_url": table_url,
|
||||
"cost_current": user_slot.cost_current if user_slot else True,
|
||||
"deposited_count": room.gate_slots.filter(status=GateSlot.FILLED).count(),
|
||||
"page_class": "page-gameboard page-room page-room-gate",
|
||||
|
||||
Reference in New Issue
Block a user