5.1 KiB
Sticker Visibility Toggle — Design Spec
Overview
Add a 3-way segmented toggle to the Picker's Game Session screen that lets admins control sticker layer visibility on the downstream vote-app overlay. The three modes are Auto (default), Show, and Hide, matching the downstream API contract defined in docs/external-downstream-stickers.md.
Motivation
The vote-app overlay shows stickers that voters purchase during a session. By default, stickers follow poll state (visible during polls, hidden between). Admins need the ability to override this — force-showing stickers between rounds for a "sticker wall showcase" or force-hiding them during a busy poll for a cleaner view.
Modes
| Mode | Behavior |
|---|---|
auto |
Default — stickers visible during polls, hidden between polls |
show |
Force stickers visible regardless of poll state |
hide |
Force stickers hidden regardless of poll state |
When a new poll starts, the downstream vote-app auto-resets mode to auto and sends a stickers.mode event.
Architecture
State ownership
stickerModestate (useState('auto')) lives in thePickercomponent, where the toggle UI renders.- A
stickerSendRefref is created inPickerand passed toSessionInfo, which populates it with a send function once the WebSocket connection is authenticated.
Props
SessionInfo receives two new props (following the existing pattern for setPollActive, setLeadingGame, etc.):
setStickerMode— setter called when inboundstickers.modeWS events arrive.stickerSendRef— ref that SessionInfo populates with(mode) => ws.send(...).
WebSocket integration
All communication uses the existing /api/sessions/live WebSocket connection managed by SessionInfo.connectWs.
Receiving (stickers.mode events):
if (message.type === 'stickers.mode') {
setStickerMode(message.mode);
return;
}
Added alongside the existing poll.* and game.* handlers in the ws.onmessage callback.
Sending (stickers.setMode commands):
After successful auth and subscribe, SessionInfo populates the ref:
stickerSendRef.current = (mode) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'stickers.setMode', data: { mode } }));
}
};
The ref is cleared on disconnect/cleanup.
No HTTP fallback. The default auto state is correct on fresh load, and the WS connection handles all sync from that point.
UI Design
Placement
The sticker toggle renders in Picker's right column (md:col-span-2), above the poll leader indicator and poll control cards. It is always visible when an active session exists, regardless of poll state.
┌─────────────────────────────────────────────┐
│ Stickers [Auto] [Show] [Hide] │
└─────────────────────────────────────────────┘
↕ poll leader indicator (conditional)
↕ currently playing card (conditional)
↕ poll control card (conditional)
↕ SessionInfo games list
Control style
A compact single-row card with:
- Label ("Stickers") on the left.
- 3-way segmented control on the right, matching the Drawing/Family Friendly filter pattern (three adjacent buttons, active one highlighted).
Segment colors
| Segment | Active background |
|---|---|
| Auto | Neutral gray (bg-gray-500) |
| Show | Green (bg-green-500) |
| Hide | Red (bg-red-500) |
Inactive segments use a muted/transparent style with border, same as the existing filter toggles.
Auto-reset pulse
When a stickers.mode event arrives with mode: "auto" and the current local state is not already auto, a brief CSS pulse animation plays on the toggle card:
- A gentle border/background highlight flash that fades over ~2 seconds.
- Implemented via a CSS
@keyframesanimation toggled by a transient state flag. - The flag is set on receiving an auto-reset event and cleared after the animation completes.
Interaction behavior
- Clicking a segment calls
stickerSendRef.current(mode)and optimistically updates local state. - If the subsequent
stickers.modeevent returns a different value, state corrects to match. - The toggle is visually disabled (reduced opacity, no pointer events) if the WebSocket is not connected.
Files changed
| File | Change |
|---|---|
frontend/src/pages/Picker.jsx |
Add stickerMode state, stickerSendRef ref, render sticker toggle card, pass new props to SessionInfo |
frontend/src/pages/Picker.jsx (SessionInfo) |
Accept setStickerMode and stickerSendRef props, handle stickers.mode WS events, populate send ref |
Scope boundaries
- In scope: Frontend toggle UI + WS message send/receive in the game picker.
- Out of scope: Backend relay of
stickers.setModemessages to the vote-app (assumed handled by existing infrastructure or a separate task). Vote-app sticker rendering (already implemented downstream).