More responsive drag and drop for card plays
This commit is contained in:
@@ -541,17 +541,17 @@ def play_card_route(
|
|||||||
if not ok:
|
if not ok:
|
||||||
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
|
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
|
||||||
|
|
||||||
# Return a success notification box that HTMX will show, or just return 204 to let polling pick it up.
|
# Return a success notification and HX-Trigger header to refresh the hand.
|
||||||
# Returning a message is nice! But returning 204 is clean. Let's return a brief HTML snippet.
|
|
||||||
color_prefix = "🟩" if res["success"] else "🟥"
|
color_prefix = "🟩" if res["success"] else "🟥"
|
||||||
drew_text = f" (Drew card: {res['drew_card']})" if res["drew_card"] else ""
|
drew_text = f" (Drew card: {res['drew_card']})" if res["drew_card"] else ""
|
||||||
return HTMLResponse(
|
drew_attr = "true" if res["drew_card"] else "false"
|
||||||
f"<div class='play-notification' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
|
html = (
|
||||||
|
f"<div class='play-notification' data-drew='{drew_attr}' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
|
||||||
f"<p>{color_prefix} {res['details']}{drew_text}</p>"
|
f"<p>{color_prefix} {res['details']}{drew_text}</p>"
|
||||||
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
|
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
|
||||||
f"</div>",
|
f"</div>"
|
||||||
headers={"HX-Trigger": "hand-updated"}
|
|
||||||
)
|
)
|
||||||
|
return HTMLResponse(html, headers={"HX-Trigger": "hand-changed"})
|
||||||
|
|
||||||
# 12. Pi-Rat plays a Joker to replace an obstacle
|
# 12. Pi-Rat plays a Joker to replace an obstacle
|
||||||
@app.post("/game/{game_id}/player/{player_id}/play-joker")
|
@app.post("/game/{game_id}/player/{player_id}/play-joker")
|
||||||
@@ -566,13 +566,13 @@ def play_joker_route(
|
|||||||
if not ok:
|
if not ok:
|
||||||
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
|
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
|
||||||
|
|
||||||
return HTMLResponse(
|
html = (
|
||||||
f"<div class='play-notification' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
|
f"<div class='play-notification' data-drew='false' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
|
||||||
f"<p>🃏 Play Joker: Discarded obstacle and drew a new replacement!</p>"
|
f"<p>🃏 Play Joker: Discarded obstacle and drew a new replacement!</p>"
|
||||||
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
|
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
|
||||||
f"</div>",
|
f"</div>"
|
||||||
headers={"HX-Trigger": "hand-updated"}
|
|
||||||
)
|
)
|
||||||
|
return HTMLResponse(html, headers={"HX-Trigger": "hand-changed"})
|
||||||
|
|
||||||
# 13. Toggle Objective Completion Checklist
|
# 13. Toggle Objective Completion Checklist
|
||||||
@app.post("/game/{game_id}/player/{player_id}/objective/toggle")
|
@app.post("/game/{game_id}/player/{player_id}/objective/toggle")
|
||||||
|
|||||||
@@ -341,9 +341,34 @@
|
|||||||
? `/game/${gameId}/player/${playerId}/play-joker`
|
? `/game/${gameId}/player/${playerId}/play-joker`
|
||||||
: `/game/${gameId}/player/${playerId}/play-card`;
|
: `/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, {
|
htmx.ajax('POST', url, {
|
||||||
values: { obstacle_id: obstacleId, card_code: cardCode },
|
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 = '<p class="empty-text text-center">Your hand is empty! (You draw cards when playing cards that match the suit color of the obstacle.)</p>';
|
||||||
|
}
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user