From 41731459a7fecad7b169942725ac9b9c46cb7a73 Mon Sep 17 00:00:00 2001 From: Tim McCarthy Date: Tue, 9 Jun 2026 19:45:56 -0700 Subject: [PATCH] More responsive drag and drop for card plays --- src/pirats/main.py | 20 +++++++++--------- src/pirats/templates/scene_partial.html | 27 ++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/pirats/main.py b/src/pirats/main.py index f08cb89..3eefc4e 100644 --- a/src/pirats/main.py +++ b/src/pirats/main.py @@ -541,17 +541,17 @@ def play_card_route( if not ok: return HTMLResponse(f"

{msg}

", status_code=400) - # Return a success notification box that HTMX will show, or just return 204 to let polling pick it up. - # Returning a message is nice! But returning 204 is clean. Let's return a brief HTML snippet. + # Return a success notification and HX-Trigger header to refresh the hand. color_prefix = "🟩" if res["success"] else "🟥" drew_text = f" (Drew card: {res['drew_card']})" if res["drew_card"] else "" - return HTMLResponse( - f"
" + drew_attr = "true" if res["drew_card"] else "false" + html = ( + f"
" f"

{color_prefix} {res['details']}{drew_text}

" f"Click to dismiss" - f"
", - headers={"HX-Trigger": "hand-updated"} + f"
" ) + return HTMLResponse(html, headers={"HX-Trigger": "hand-changed"}) # 12. Pi-Rat plays a Joker to replace an obstacle @app.post("/game/{game_id}/player/{player_id}/play-joker") @@ -566,13 +566,13 @@ def play_joker_route( if not ok: return HTMLResponse(f"

{msg}

", status_code=400) - return HTMLResponse( - f"
" + html = ( + f"
" f"

🃏 Play Joker: Discarded obstacle and drew a new replacement!

" f"Click to dismiss" - f"
", - headers={"HX-Trigger": "hand-updated"} + f"
" ) + return HTMLResponse(html, headers={"HX-Trigger": "hand-changed"}) # 13. Toggle Objective Completion Checklist @app.post("/game/{game_id}/player/{player_id}/objective/toggle") diff --git a/src/pirats/templates/scene_partial.html b/src/pirats/templates/scene_partial.html index 46ecbfe..6fe7854 100644 --- a/src/pirats/templates/scene_partial.html +++ b/src/pirats/templates/scene_partial.html @@ -341,9 +341,34 @@ ? `/game/${gameId}/player/${playerId}/play-joker` : `/game/${gameId}/player/${playerId}/play-card`; + // Find the dragged card element before the request + const draggedCard = document.querySelector( + `.card-large[data-card-code="${cardCode}"]` + ); + htmx.ajax('POST', url, { values: { obstacle_id: obstacleId, card_code: cardCode }, - swap: 'none' + target: 'body', + swap: 'beforeend' + }).then(function() { + // Remove the card from the hand on success + if (draggedCard && draggedCard.parentNode) { + draggedCard.remove(); + } + // Check if hand is now empty (and no card was drawn) + const handFlex = document.querySelector('.hand-flex'); + if (handFlex && handFlex.children.length === 0) { + handFlex.innerHTML = '

Your hand is empty! (You draw cards when playing cards that match the suit color of the obstacle.)

'; + } + // Auto-dismiss the notification after 3 seconds + const notif = document.querySelector('.play-notification:last-of-type'); + if (notif) { + setTimeout(function() { notif.remove(); }, 3000); + // If a card was drawn, refresh the full view to show it + if (notif.getAttribute('data-drew') === 'true') { + htmx.trigger(document.body, 'phase-changed'); + } + } }); } }