All acceptance criteria have been implemented and tested.
- Component:
RecentParticipants.tsx - Polling: Every 15 seconds via
GET /raffles/:id/participants?since=<timestamp> - Incremental Updates: Only fetches new participants since last poll
- Capping: Displays max 20 participants, drops oldest as new arrive
- Instant Display: User's purchase appears immediately before API confirmation
- Visual Indicator: Green pulse badge shows pending status
- Tooltip: Hover shows "(pending)" label
- Confirmation: Optimistic flag removed when API confirms
- Slide-In: CSS keyframes
slideInDown(300ms, ease-out) - Staggered: 50ms delay between each entry
- Accessible: Respects
prefers-reduced-motion: reducemedia query - No Animation: Disabled when user prefers reduced motion
- Route:
GET /raffles/:id/participants - Query Param:
since(unix timestamp in ms) - Response: Array of
{ address: string; timestamp: number } - Placeholder: Ready for indexer integration
| Criterion | Status | Implementation |
|---|---|---|
| New participants appear without refresh (within 15s) | ✅ | 15s polling interval with incremental fetches |
| New entries animate in from top | ✅ | CSS keyframes with staggered delays |
| Animation skipped when prefers-reduced-motion: reduce | ✅ | Media query detection and conditional styling |
| Optimistic self-entry appears instantly | ✅ | __addOptimisticParticipant integration |
client/src/components/
├── RecentParticipants.tsx (NEW) — 250 lines
└── RecentParticipants.spec.tsx (NEW) — 150 lines
client/src/pages/
└── RafflePage.tsx (MODIFIED) — Added imports, handler, component
backend/src/api/rest/raffles/
├── raffles.controller.ts (MODIFIED) — Added GET /raffles/:id/participants
└── raffles.service.ts (MODIFIED) — Added getRecentParticipants method
IMPLEMENTATION_LIVE_PARTICIPANTS.md (NEW) — 250 lines
ISSUE_486_SUMMARY.md (NEW) — This file
interface RecentParticipantsProps {
raffleId: number;
currentUserAddress?: string;
onOptimisticUpdate?: (address: string) => void;
}Features:
- Automatic polling every 15 seconds
- Optimistic update via
window.__addOptimisticParticipant(address) - Deterministic avatar colors from address hash
- Hover tooltips with full address
- Loading and error states
- Respects accessibility preferences
const handleTicketPurchase = () => {
if (address && (window as any).__addOptimisticParticipant) {
(window as any).__addOptimisticParticipant(address);
}
// ... proceed with purchase
};# Get all participants
GET /raffles/1/participants
# Get participants since timestamp
GET /raffles/1/participants?since=1716950000000
# Response
[
{
"address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5V3VQ",
"timestamp": 1716950000000
},
...
]- ✅ Loading state
- ✅ Participant display
- ✅ Polling interval (15s)
- ✅ Optimistic updates
- ✅ Animation respect for
prefers-reduced-motion - ✅ Capping at 20 participants
- ✅ Error handling
- Open RafflePage
- Wait 15 seconds, verify new participants appear
- Click "Buy Tickets", verify address appears instantly
- Observe smooth slide-in animation
- Enable
prefers-reduced-motion: reduce, verify no animation - Verify max 20 avatars displayed
- Test error handling (disconnect API)
- Polling: 15-second interval balances freshness vs. server load
- Capping: Max 20 participants prevents DOM bloat
- Incremental Fetches:
sinceparameter reduces payload - Optimistic Updates: No extra API calls, instant UX
- Animations: GPU-accelerated CSS transforms
- WebSocket: Replace polling with real-time updates
- Indexer Integration: Query blockchain events for actual ticket purchases
- Pagination: "View All" link to show all participants
- Filtering: Filter by time range, address prefix
- Analytics: Track participant engagement
- Notifications: Notify user when purchase confirmed
- No breaking changes
- Backward compatible with existing RafflePage
- Graceful degradation if API endpoint unavailable
- New endpoint, no changes to existing APIs
- Placeholder implementation ready for indexer integration
- No database migrations required
If issues arise:
git revert 11b321b- Branch:
feat/486-live-participants-feed - PR: #641
- Commit:
11b321b - Files: 6 changed, 641 insertions(+), 32 deletions(-)
- Issue: #486
- PR: #641
- Component:
client/src/components/RecentParticipants.tsx - Page:
client/src/pages/RafflePage.tsx - Endpoint:
GET /raffles/:id/participants - Guide:
IMPLEMENTATION_LIVE_PARTICIPANTS.md