bud page sea-btn cascade: live-invite window + accept-on-GET + glow handoff; my-buds tooltip clamp + row hover/lock — TDD
Fixes the Bill Bud invite cascade so the sea sub-btn actually lights + leads to the bud's my_sea, and gives the My Buds row tooltip viewport clamping + hover/lock styling. SeaInvite.invitee_access_open (gameboard/models.py): new invitee-facing access window — a non-terminal invite (PENDING/ACCEPTED) within 24h of being proffered OR within 24h of the invitee's last gate token deposit. Re-arms on each deposit; DECLINED/LEFT/EXPIRED stay shut. Distinct from is_expired (which only models the PENDING lapse). 8 UTs. bud_page (billboard/views.py): sea_btn_active / sea_first_draw_pending now key on invitee_access_open across PENDING + ACCEPTED, not PENDING-only. Old design darkened the btn the instant the user accepted, so they could never reach the bud's sea from here post-accept — that was the red .fa-ban the user saw. ITs updated: accepted-within-window now lights; added stale-accepted-dark + recent-deposit-relights cases. my_sea_visit (gameboard/views.py): accept-on-GET — a still-pending, non-expired invite from the owner to the visitor is accepted implicitly on arrival (the sea-btn cascade + @mailman post-attribution anchor both land here, so the click IS the acceptance). Previously PENDING → 403, so the cascade dead-ended. ITs: pending-invitee now auto-accepts (200); expired-pending still 403s; stranger still 403s. bud.html: burger → sea_btn glow-handoff machine (the my_sea.html cascade minus the spread-modal stage) so the glow rides the affordance chain to the click target; active sea click clears glow, preserves .active, navigates. my-buds-tooltip.js: clamp the position:fixed #id_tooltip_portal to the viewport on row-lock — same 1rem-inset shape as game-kit.js / sky-wheel.js / wallet.js (measure after .active, clamp left, prefer above / flip below). Reset on clear. _billboard.scss: .bud-entry hover + .row-locked highlight (rows aren't .row-3col so the existing rule missed them) — fill --secUser, flip the --terUser handle to --quiUser, trailing title to readable --priUser. 520 billboard+gameboard ITs/UTs green; affected sea-btn-cascade FT green. Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -896,11 +896,26 @@ def my_sea_visit(request, owner_id):
|
||||
their own my_sea. Renders the table hex (seat 1C = owner-drawn, seat 2C =
|
||||
this visitor once present) + the owner's draw read-only via
|
||||
`latest_draw_slots`. No AUTO DRAW / DEL / FLIP-to-deposit on the owner's
|
||||
hand — `sea_btn_active` forced False."""
|
||||
hand — `sea_btn_active` forced False.
|
||||
|
||||
Accept-on-GET (user-spec 2026-05-29): a still-pending, non-expired invite
|
||||
from `owner` to this user is accepted implicitly on arrival — the bud-page
|
||||
sea-btn cascade (+ the @mailman post-attribution anchor) both land here,
|
||||
and the click IS the acceptance. A stranger with no invite still 403s."""
|
||||
from apps.lyric.models import User
|
||||
from .models import SeaInvite
|
||||
owner = get_object_or_404(User, id=owner_id)
|
||||
if owner == request.user:
|
||||
return redirect("my_sea")
|
||||
pending = (
|
||||
SeaInvite.objects
|
||||
.filter(owner=owner, invitee=request.user, status=SeaInvite.PENDING)
|
||||
.order_by("-created_at").first()
|
||||
)
|
||||
if pending is not None and not pending.is_expired:
|
||||
pending.status = SeaInvite.ACCEPTED
|
||||
pending.accepted_at = timezone.now()
|
||||
pending.save(update_fields=["status", "accepted_at"])
|
||||
invite = _accepted_visit_invite(owner, request.user)
|
||||
if invite is None:
|
||||
return HttpResponseForbidden()
|
||||
|
||||
Reference in New Issue
Block a user