game room voice: gate on >1 cost-current depositor over the 7d initial period (not grace), not the phase — solo/CARTE-all-6 stays off — TDD
Refines the room voice activation per user-spec: voice mirrors my_sea's window but over the seat's 7d INITIAL cost period (GateSlot.cost_current — within [filled_at, filled_at+7d), NOT the renewal grace). voice_active now needs the gate CLOSED (table_status set = ROLE_SELECT onset) + the viewer seated + MORE THAN ONE distinct gamer holding a FILLED, cost-current seat. A sole depositor — including a CARTE owner occupying all 6 seats (one gamer) — keeps voice OFF (no one to talk to); a 2nd qualifying gamer flips it on; it toggles back off once the cost period lapses into grace. Replaces the earlier phase-set gate (ROLE/SIG/SKY only) — voice now spans the whole 7d window incl. IN_GAME (the phase ceiling at SEA_SELECT made the 7d expiry unreachable). TDD: RoomVoiceActivationTest reworked (13 ITs) — active w. 6 depositors across ROLE/SIG/SKY + persists in IN_GAME; inactive for a single depositor (CARTE-all-6), flips on once a 2nd gamer qualifies, inactive when filled 8d ago (grace), inactive before the gate closes, inactive for a non-seated viewer; room-id / muted-at passthrough + active-btn markup unchanged. 736 epic+drama ITs green. [[project-my-sea-invite-voice-blueprint]] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -820,18 +820,30 @@ def room_view(request, room_id):
|
||||
seen_seated.add(seat.gamer_id)
|
||||
seated_others.append(seat.gamer)
|
||||
ctx["seated_others"] = seated_others
|
||||
# Voice (Phase C, room path) — the burger fan's #id_voice_btn lights for a
|
||||
# SEATED gamer across the whole character-creation arc: ROLE_SELECT onset
|
||||
# (all tokens committed, seats pre-created by pick_roles) continuously through
|
||||
# SKY_SELECT (which hosts the in-page DRAW SEA felt), going dark at IN_GAME.
|
||||
# The room UUID is the WebRTC mesh key — RoomVoiceConsumer._can_join already
|
||||
# gates an epic room on TableSeat membership (no consumer change), and
|
||||
# voice-mesh.js / burger-btn.js bind the active click → join/toggle-mute.
|
||||
# voice_muted_at carries the persisted mute so an in-arc nav/refresh rejoins
|
||||
# MUTED (mirrors my_sea). [[project-my-sea-invite-voice-blueprint]]
|
||||
VOICE_PHASES = {Room.ROLE_SELECT, Room.SIG_SELECT, Room.SKY_SELECT}
|
||||
# Voice (Phase C, room path) — the burger fan's #id_voice_btn. Mirrors my_sea's
|
||||
# voice window, but over the seat's 7d INITIAL cost period (GateSlot.cost_current
|
||||
# — NOT the renewal grace, mirroring "lasts a day, off in grace" at 7d scale).
|
||||
# It needs MORE THAN ONE distinct gamer holding a token-backed, cost-current
|
||||
# seat: a SOLE depositor — including a CARTE owner occupying all 6 seats (one
|
||||
# gamer) — has no one to talk to, so voice stays OFF; a 2nd qualifying gamer
|
||||
# flips it on, and it toggles back off once the cost period lapses into grace.
|
||||
# Gated on the gate being CLOSED (table_status set = ROLE_SELECT onset) + the
|
||||
# viewer being seated. The room UUID is the WebRTC mesh key —
|
||||
# RoomVoiceConsumer._can_join already gates an epic room on TableSeat
|
||||
# membership (no consumer change); voice_muted_at carries the persisted mute so
|
||||
# an in-period nav/refresh rejoins MUTED. [[project-my-sea-invite-voice-blueprint]]
|
||||
active_depositors = {
|
||||
slot.gamer_id
|
||||
for slot in room.gate_slots.filter(
|
||||
status=GateSlot.FILLED, gamer__isnull=False)
|
||||
if slot.cost_current
|
||||
}
|
||||
is_seated = room.table_seats.filter(gamer=request.user).exists()
|
||||
ctx["voice_active"] = is_seated and room.table_status in VOICE_PHASES
|
||||
ctx["voice_active"] = (
|
||||
room.table_status is not None
|
||||
and is_seated
|
||||
and len(active_depositors) > 1
|
||||
)
|
||||
ctx["voice_room_id"] = str(room.id)
|
||||
ctx["voice_muted_at"] = (
|
||||
request.user.voice_muted_at.isoformat()
|
||||
|
||||
Reference in New Issue
Block a user