live updates

This commit is contained in:
2026-01-29 02:16:24 -05:00
parent 864cbaece9
commit efa1ea3b45
12 changed files with 423 additions and 69 deletions
+55
View File
@@ -2,12 +2,14 @@ import React, { useState, useEffect, useRef } from 'react';
import { useParams, Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import { useAuth } from '../AuthContext';
import { useSocket } from '../SocketContext';
import api from '../api';
import { useClickOutside } from '../hooks/useClickOutside';
export default function ChallengeDetail() {
const { id } = useParams();
const { user } = useAuth();
const { socket, joinChallenge, leaveChallenge } = useSocket();
const [challenge, setChallenge] = useState(null);
const [predictions, setPredictions] = useState([]);
const [leaderboard, setLeaderboard] = useState([]);
@@ -29,6 +31,59 @@ export default function ChallengeDetail() {
loadLeaderboard();
}, [id]);
// Join challenge room for real-time updates
useEffect(() => {
if (socket && id) {
joinChallenge(id);
return () => {
leaveChallenge(id);
};
}
}, [socket, id]);
// Listen for real-time prediction events
useEffect(() => {
if (!socket) return;
const handlePredictionCreated = (prediction) => {
setPredictions(prev => [prediction, ...prev]);
toast.success(`New prediction from ${prediction.username}`);
};
const handlePredictionValidated = (prediction) => {
setPredictions(prev =>
prev.map(p => p.id === prediction.id ? prediction : p)
);
loadLeaderboard(); // Refresh leaderboard when points change
if (prediction.user_id === user.id) {
toast.success(
prediction.status === 'validated'
? '🎉 Your prediction was validated!'
: '❌ Your prediction was invalidated'
);
}
};
const handleInvitationResponse = (response) => {
if (response.status === 'accepted') {
toast.success(`${response.username} joined the challenge!`);
loadChallenge(); // Refresh participant list
}
};
socket.on('prediction:created', handlePredictionCreated);
socket.on('prediction:validated', handlePredictionValidated);
socket.on('challenge:invitation_response', handleInvitationResponse);
return () => {
socket.off('prediction:created', handlePredictionCreated);
socket.off('prediction:validated', handlePredictionValidated);
socket.off('challenge:invitation_response', handleInvitationResponse);
};
}, [socket, user]);
const loadChallenge = async () => {
try {
const data = await api.getChallenge(id);