post applet: unify header across post.html + reelhouse chat; seat-based recipients/access; fix async chip styling
Unifies the Post applet across post.html and the room game-views POST chat: - Extract _post_recipient.html (the @handle chip: post-recipient + post-attribution) + _post_header.html (title + 'shared between … & me' / 'just me' prose). post.html's owner branch + the reelhouse POST view both render via _post_header; the invitee branch stays bespoke but reuses the chip. - Async-chip styling bug (post.html): the bud-invite append built a bare <span class=post-recipient> with the raw display name, so the recipient rendered without the --quaUser key + the @ until a refresh. Now billboard:share_post returns recipient_chip_html (the server-rendered _post_recipient.html) and the bud panel splices THAT in — identical classes + @handle. Also fixed the 'just me'→'& me' flip to mutate only the leading text node so the self line's own .post-attribution span survives. - Reelhouse POST chat: gains the full .post-header — title hardcoded to 'Gamer Introduction' (dynamic template later) + recipients = the gamers OCCUPYING SEATS (room.table_seats, deduped, viewer excluded), NOT gate-slot/token depositors. And room_post ACCESS now requires a TableSeat, not a filled gate slot: a depositor who never took a seat can retract + leave, so they must not have R/W access to the private chat. Tests: header IT (seatmate listed, transient gate-slot-only depositor not — scoped to the recipients paragraph since the position strip carries every gate-slot handle elsewhere); room_post seat-access ITs (seated OK; non-seated + gate-slot-only → 403); share_post recipient_chip_html IT; carousel FT setUp now seats disco/amigo/bud (pal/dude/bro stay transient). All green: 255 ITs, 11 carousel FTs, 29 bud-btn/composer FTs. [[project-room-game-views-carousel]] [[feedback-at-handle-for-usernames]] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -665,6 +665,19 @@ def room_view(request, room_id):
|
||||
ctx["room_post"] = room_post
|
||||
ctx["room_post_lines"] = room_post.lines.select_related("author").all()
|
||||
ctx["text_btn_active"] = True
|
||||
# The POST chat's participants are the gamers OCCUPYING SEATS (a TableSeat
|
||||
# with their gamer) — NOT mere gate-slot/token depositors, who can retract
|
||||
# their token + leave without ever committing to the game. `seated_others`
|
||||
# excludes the viewer (who is the "& me" line); deduped, slot-ordered.
|
||||
seen_seated = set()
|
||||
seated_others = []
|
||||
for seat in (room.table_seats.filter(gamer__isnull=False)
|
||||
.exclude(gamer=request.user)
|
||||
.select_related("gamer").order_by("slot_number")):
|
||||
if seat.gamer_id not in seen_seated:
|
||||
seen_seated.add(seat.gamer_id)
|
||||
seated_others.append(seat.gamer)
|
||||
ctx["seated_others"] = seated_others
|
||||
return render(request, "apps/gameboard/room.html", ctx)
|
||||
|
||||
|
||||
@@ -680,10 +693,10 @@ def room_post(request, room_id):
|
||||
room = Room.objects.get(id=room_id)
|
||||
if request.method != "POST":
|
||||
return redirect("epic:room", room_id=room_id)
|
||||
# Only gamers holding a filled seat at this table may post to its thread.
|
||||
if not room.gate_slots.filter(
|
||||
gamer=request.user, status=GateSlot.FILLED
|
||||
).exists():
|
||||
# Only gamers OCCUPYING A SEAT (a TableSeat with their gamer) may speak in
|
||||
# the chat — NOT gate-slot/token depositors who haven't committed to a seat
|
||||
# (they can retract + leave, so they must not have R/W access to the chat).
|
||||
if not room.table_seats.filter(gamer=request.user).exists():
|
||||
return HttpResponseForbidden()
|
||||
post = room.get_thread_post()
|
||||
form = ExistingPostLineForm(for_post=post, data=request.POST)
|
||||
|
||||
Reference in New Issue
Block a user