offloaded Significator FTs into FTs.test_room_sig_select; new sig-select.js imported into room.html; new apps.epic.consumers & .views, ITs to confirm functionality
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This commit is contained in:
@@ -25,3 +25,6 @@ class RoomConsumer(AsyncJsonWebsocketConsumer):
|
||||
|
||||
async def roles_revealed(self, event):
|
||||
await self.send_json(event)
|
||||
|
||||
async def sig_selected(self, event):
|
||||
await self.send_json(event)
|
||||
|
||||
96
src/apps/epic/static/apps/epic/sig-select.js
Normal file
96
src/apps/epic/static/apps/epic/sig-select.js
Normal file
@@ -0,0 +1,96 @@
|
||||
var SigSelect = (function () {
|
||||
var SIG_ORDER = ['PC', 'NC', 'EC', 'SC', 'AC', 'BC'];
|
||||
|
||||
var sigDeck, selectUrl, userRole;
|
||||
|
||||
function getActiveRole() {
|
||||
for (var i = 0; i < SIG_ORDER.length; i++) {
|
||||
var seat = document.querySelector('.table-seat[data-role="' + SIG_ORDER[i] + '"]');
|
||||
if (seat && !seat.dataset.sigDone) return SIG_ORDER[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isEligible() {
|
||||
return !!(userRole && userRole === getActiveRole());
|
||||
}
|
||||
|
||||
function getCsrf() {
|
||||
var m = document.cookie.match(/csrftoken=([^;]+)/);
|
||||
return m ? m[1] : '';
|
||||
}
|
||||
|
||||
function applySelection(cardId, role, deckType) {
|
||||
// Remove only the specific pile copy (levity or gravity) of this card
|
||||
var selector = '.sig-card.' + deckType + '-deck[data-card-id="' + cardId + '"]';
|
||||
sigDeck.querySelectorAll(selector).forEach(function (c) { c.remove(); });
|
||||
|
||||
// Mark this seat done, remove active
|
||||
var seat = document.querySelector('.table-seat[data-role="' + role + '"]');
|
||||
if (seat) {
|
||||
seat.classList.remove('active');
|
||||
seat.dataset.sigDone = '1';
|
||||
}
|
||||
|
||||
// Advance active to next seat
|
||||
var nextRole = getActiveRole();
|
||||
if (nextRole) {
|
||||
var nextSeat = document.querySelector('.table-seat[data-role="' + nextRole + '"]');
|
||||
if (nextSeat) nextSeat.classList.add('active');
|
||||
}
|
||||
|
||||
// Place a card placeholder in inventory
|
||||
var invSlot = document.getElementById('id_inv_sig_card');
|
||||
if (invSlot) {
|
||||
var card = document.createElement('div');
|
||||
card.className = 'card';
|
||||
invSlot.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
sigDeck = document.getElementById('id_sig_deck');
|
||||
if (!sigDeck) return;
|
||||
selectUrl = sigDeck.dataset.selectSigUrl;
|
||||
userRole = sigDeck.dataset.userRole;
|
||||
|
||||
sigDeck.addEventListener('click', function (e) {
|
||||
var card = e.target.closest('.sig-card');
|
||||
if (!card) return;
|
||||
if (!isEligible()) return;
|
||||
var activeRole = getActiveRole();
|
||||
var cardId = card.dataset.cardId;
|
||||
var deckType = card.dataset.deck;
|
||||
window.showGuard(card, 'Select this significator?', function () {
|
||||
fetch(selectUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-CSRFToken': getCsrf(),
|
||||
},
|
||||
body: 'card_id=' + encodeURIComponent(cardId) + '&deck_type=' + encodeURIComponent(deckType),
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
applySelection(cardId, activeRole, deckType);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('room:sig_selected', function (e) {
|
||||
if (!sigDeck) return;
|
||||
var cardId = String(e.detail.card_id);
|
||||
var role = e.detail.role;
|
||||
var deckType = e.detail.deck_type;
|
||||
// Idempotent — skip if this copy already removed (local selector already did it)
|
||||
if (!sigDeck.querySelector('.sig-card.' + deckType + '-deck[data-card-id="' + cardId + '"]')) return;
|
||||
applySelection(cardId, role, deckType);
|
||||
});
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}());
|
||||
@@ -902,7 +902,7 @@ class SelectSigCardViewTest(TestCase):
|
||||
def test_select_sig_notifies_ws(self):
|
||||
with patch("apps.epic.views._notify_sig_selected") as mock_notify:
|
||||
self._post()
|
||||
mock_notify.assert_called_once_with(self.room.id)
|
||||
mock_notify.assert_called_once()
|
||||
|
||||
def test_select_sig_requires_login(self):
|
||||
self.client.logout()
|
||||
|
||||
@@ -64,10 +64,10 @@ def _notify_role_select_start(room_id):
|
||||
)
|
||||
|
||||
|
||||
def _notify_sig_selected(room_id):
|
||||
def _notify_sig_selected(room_id, card_id, role, deck_type='levity'):
|
||||
async_to_sync(get_channel_layer().group_send)(
|
||||
f'room_{room_id}',
|
||||
{'type': 'sig_selected'},
|
||||
{'type': 'sig_selected', 'card_id': str(card_id), 'role': role, 'deck_type': deck_type},
|
||||
)
|
||||
|
||||
|
||||
@@ -194,7 +194,9 @@ def _role_select_context(room, user):
|
||||
ctx["user_seat"] = user_seat
|
||||
ctx["partner_seat"] = partner_seat
|
||||
ctx["revealed_seats"] = room.table_seats.filter(role_revealed=True).order_by("slot_number")
|
||||
ctx["sig_cards"] = sig_deck_cards(room)
|
||||
raw_sig_cards = sig_deck_cards(room)
|
||||
half = len(raw_sig_cards) // 2
|
||||
ctx["sig_cards"] = [(c, 'levity') for c in raw_sig_cards[:half]] + [(c, 'gravity') for c in raw_sig_cards[half:]]
|
||||
ctx["sig_seats"] = sig_seat_order(room)
|
||||
ctx["sig_active_seat"] = active_sig_seat(room)
|
||||
return ctx
|
||||
@@ -500,7 +502,8 @@ def select_sig(request, room_id):
|
||||
return HttpResponse(status=409)
|
||||
active_seat.significator = card
|
||||
active_seat.save()
|
||||
_notify_sig_selected(room_id)
|
||||
deck_type = request.POST.get('deck_type', 'levity')
|
||||
_notify_sig_selected(room_id, card.pk, active_seat.role, deck_type)
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user