various styling & structural changes to unify site themes; token-drop interaction changes across epic urls & views
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
Disco DeDisco
2026-03-14 02:03:44 -04:00
parent d9feb80b2a
commit f76c6d0fe5
8 changed files with 618 additions and 90 deletions

View File

@@ -1,11 +1,56 @@
from datetime import timedelta
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.utils import timezone
from apps.epic.models import Room, RoomInvite, debit_token
from apps.epic.models import GateSlot, Room, RoomInvite, debit_token
from apps.lyric.models import Token
RESERVE_TIMEOUT = timedelta(seconds=60)
def _expire_reserved_slots(room):
cutoff = timezone.now() - RESERVE_TIMEOUT
room.gate_slots.filter(
status=GateSlot.RESERVED,
reserved_at__lt=cutoff,
).update(status=GateSlot.EMPTY, gamer=None, reserved_at=None)
def _gate_context(room, user):
_expire_reserved_slots(room)
slots = room.gate_slots.order_by("slot_number")
pending_slot = slots.filter(status=GateSlot.RESERVED).first()
user_reserved_slot = None
user_filled_slot = None
if user.is_authenticated:
user_reserved_slot = slots.filter(gamer=user, status=GateSlot.RESERVED).first()
user_filled_slot = slots.filter(gamer=user, status=GateSlot.FILLED).first()
can_drop = (
user.is_authenticated
and pending_slot is None
and user_reserved_slot is None
and user_filled_slot is None
)
is_last_slot = (
user_reserved_slot is not None
and slots.filter(status=GateSlot.EMPTY).count() == 0
)
user_can_reject = user_reserved_slot is not None or user_filled_slot is not None
return {
"slots": slots,
"pending_slot": pending_slot,
"user_reserved_slot": user_reserved_slot,
"user_filled_slot": user_filled_slot,
"can_drop": can_drop,
"is_last_slot": is_last_slot,
"user_can_reject": user_can_reject,
}
@login_required
def create_room(request):
if request.method == "POST":
@@ -15,33 +60,68 @@ def create_room(request):
return redirect("epic:gatekeeper", room_id=room.id)
return redirect("/gameboard/")
def gatekeeper(request, room_id):
room = Room.objects.get(id=room_id)
slots = room.gate_slots.order_by("slot_number")
user_has_slot = (
request.user.is_authenticated
and room.gate_slots.filter(gamer=request.user).exists()
)
return render(request, "apps/gameboard/room.html", {
"room": room,
"slots": slots,
"user_has_slot": user_has_slot,
})
ctx = _gate_context(room, request.user)
ctx["room"] = room
return render(request, "apps/gameboard/room.html", ctx)
@login_required
def drop_token(request, room_id, slot_number):
def drop_token(request, room_id):
if request.method == "POST":
room = Room.objects.get(id=room_id)
slot = room.gate_slots.get(slot_number=slot_number)
token = (
request.user.tokens.filter(token_type=Token.COIN).first()
or request.user.tokens.filter(token_type=Token.FREE).first()
or request.user.tokens.filter(token_type=Token.TITHE).first()
)
if token:
debit_token(request.user, slot, token)
if room.gate_slots.filter(status=GateSlot.RESERVED).exists():
return redirect("epic:gatekeeper", room_id=room_id)
if room.gate_slots.filter(gamer=request.user, status=GateSlot.FILLED).exists():
return redirect("epic:gatekeeper", room_id=room_id)
slot = room.gate_slots.filter(
status=GateSlot.EMPTY
).order_by("slot_number").first()
if slot:
slot.gamer = request.user
slot.status = GateSlot.RESERVED
slot.reserved_at = timezone.now()
slot.save()
return redirect("epic:gatekeeper", room_id=room_id)
@login_required
def confirm_token(request, room_id):
if request.method == "POST":
room = Room.objects.get(id=room_id)
slot = room.gate_slots.filter(
gamer=request.user, status=GateSlot.RESERVED
).first()
if slot:
token = (
request.user.tokens.filter(token_type=Token.COIN).first()
or request.user.tokens.filter(token_type=Token.FREE).first()
or request.user.tokens.filter(token_type=Token.TITHE).first()
)
if token:
debit_token(request.user, slot, token)
return redirect("epic:gatekeeper", room_id=room_id)
@login_required
def reject_token(request, room_id):
if request.method == "POST":
room = Room.objects.get(id=room_id)
slot = room.gate_slots.filter(
gamer=request.user,
status__in=[GateSlot.RESERVED, GateSlot.FILLED],
).first()
if slot:
slot.gamer = None
slot.status = GateSlot.EMPTY
slot.reserved_at = None
slot.filled_at = None
slot.save()
return redirect("epic:gatekeeper", room_id=room_id)
@login_required
def invite_gamer(request, room_id):
if request.method == "POST":
@@ -56,6 +136,7 @@ def invite_gamer(request, room_id):
)
return redirect("epic:gatekeeper", room_id=room_id)
@login_required
def delete_room(request, room_id):
if request.method == "POST":
@@ -64,6 +145,7 @@ def delete_room(request, room_id):
room.delete()
return redirect("/gameboard/")
@login_required
def abandon_room(request, room_id):
if request.method == "POST":
@@ -77,17 +159,11 @@ def abandon_room(request, room_id):
).delete()
return redirect("/gameboard/")
def gate_status(request, room_id):
room = Room.objects.get(id=room_id)
if room.gate_status == Room.OPEN:
return HttpResponse("")
slots = room.gate_slots.order_by("slot_number")
user_has_slot = (
request.user.is_authenticated
and slots.filter(gamer=request.user).exists()
)
return render(request, "apps/gameboard/_partials/_gatekeeper.html", {
"room": room,
"slots": slots,
"user_has_slot": user_has_slot,
})
ctx = _gate_context(room, request.user)
ctx["room"] = room
return render(request, "apps/gameboard/_partials/_gatekeeper.html", ctx)