fix(call-to-play): expire elapsed calls clearly
Deadline completion previously shared the green Ready presentation with a full roster and remained visible forever. An abandoned call therefore looked ready to launch and required its creator to return and cancel it. Give elapsed calls a distinct Time's up state and a five-minute grace period in which the creator can start or extend them. After that, both the reducer and peer store remove the call as a unit. Filled calls remain ready until their deadline, and active calls continue to retain complete history for late joiners. Test Plan: - `just fmt` -- passed - `just clippy` -- passed - `just test` -- passed, 147 peer tests - `just frontend-test` -- passed, 22 tests - `just build` -- passed - `git diff --cached --check` -- passed
This commit is contained in:
+16
-9
@@ -457,6 +457,10 @@ flips into the `checkin` phase: it lights up everywhere and everyone who said
|
||||
"I'm in" is nudged to answer with the same `Ready now` / `+N minutes` states as
|
||||
a play-now call. A play-now call is effectively "always in its check-in window."
|
||||
|
||||
When the deadline passes, the call is labeled **Time's up** rather than Ready.
|
||||
It remains visible for five minutes so the caller can start or extend it, then
|
||||
the call and its history expire as a unit.
|
||||
|
||||
**Every call carries a small group chat** (see "Per-call chat" below).
|
||||
|
||||
### Three surfaces
|
||||
@@ -478,12 +482,13 @@ Two derived values drive everything (`calltoplay.jsx`):
|
||||
|
||||
- `phaseOf(call)` → `'now'` (no `scheduledFor`) · `'scheduled'` (>15 min out) ·
|
||||
`'checkin'` (within the 15-min lead).
|
||||
- `statusOf(call)` (quick-bar/label status) → `'started'` · `'ready'`
|
||||
(`readyCount >= maxPlayers`, or state `done`) · `'soon'` (`deadline - now ≤
|
||||
15 min`) · `'scheduled'` (has a clock time) · `'call'` (a plain play-now call).
|
||||
- `statusOf(call)` (quick-bar/label status) → `'started'` · `'expired'`
|
||||
(deadline elapsed) · `'ready'` (`readyCount >= maxPlayers`, or state `done`) ·
|
||||
`'soon'` (`deadline - now ≤ 15 min`) · `'scheduled'` (has a clock time) ·
|
||||
`'call'` (a plain play-now call).
|
||||
|
||||
Quick-bar labels + LED colors: **SCHEDULED**, **CALL TO PLAY**, **STARTING
|
||||
SOON**, **READY** (`TICKER_LABEL`), each with its own dot color via
|
||||
SOON**, **READY**, **TIME'S UP** (`TICKER_LABEL`), each with its own dot color via
|
||||
`.ctp-ticker-dot[data-status]`.
|
||||
|
||||
**Per-participant ready state:** `ready` (explicitly readied, or their `readyAt`
|
||||
@@ -497,7 +502,7 @@ as larger `AvatarChip`s in the nomination card roster.
|
||||
|
||||
Top to bottom:
|
||||
|
||||
1. **Header** — square game cover + title + a sub-line: `Called by <creator> · N/M peers have it installed` (or `Scheduled by <creator> · starts at HH:MM · …`), and a **timer** on the right: a live `M:SS` countdown for play-now/check-in, the **clock time + "in N min"** for a scheduled call, `Ready`, or `Launching…`. Countdown urgency (`data-urgency` high/mid/low) tints it as time runs low.
|
||||
1. **Header** — square game cover + title + a sub-line: `Called by <creator> · N/M peers have it installed` (or `Scheduled by <creator> · starts at HH:MM · …`), and a **timer** on the right: a live `M:SS` countdown for play-now/check-in, the **clock time + "in N min"** for a scheduled call, `Ready`, `Time's up`, or `Launching…`. If catalog data is temporarily unavailable, the card still renders the caller, game ID, roster, chat, and coordination actions with a clear `Game unavailable here` label. Countdown urgency (`data-urgency` high/mid/low) tints it as time runs low.
|
||||
2. **Check-in note** — only in the `checkin` phase: a clock icon + "Starting soon — check-in is open" (or a personalized nudge if you RSVP'd).
|
||||
3. **Progress bar** — time remaining as a fill (accent, → green when done); hidden while a call is still in the far-out `scheduled` phase.
|
||||
4. **Roster** — `readyCount/maxPlayers ready` (scheduled shows `N in · up to M players`; check-in adds `· K not checked in yet`), then avatar chips for each participant plus empty slots up to `maxPlayers`.
|
||||
@@ -564,10 +569,12 @@ The production launcher uses the peer's existing QUIC control channel. Each
|
||||
create, response, RSVP, chat, leave, cancel, start, or deadline-extension action
|
||||
is an immutable, uniquely identified event. Connected peers receive new events
|
||||
immediately, while `Hello` / `HelloAck` exchange the bounded, deduplicated event
|
||||
history so a late joiner reconstructs the same calls. The frontend reducer turns
|
||||
that event history into the `Nomination` state above and derives time-based phase
|
||||
changes locally. The `username` that identifies "you" comes from
|
||||
`settings.username` (the Profile setting), not a prop default.
|
||||
history so a late joiner reconstructs every event and chat message for active
|
||||
calls. Started and cancelled calls compact to terminal tombstones; expired
|
||||
calls are removed after the five-minute grace period. The frontend reducer
|
||||
turns that event history into the `Nomination` state above and derives
|
||||
time-based phase changes locally. Stable peer IDs identify actors and enforce
|
||||
creator controls; `settings.username` is only the display name.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user