import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import toast from 'react-hot-toast'; import api from '../api'; import { useClickOutside } from '../hooks/useClickOutside'; import { useSocket } from '../SocketContext'; export default function ChallengeList() { const [challenges, setChallenges] = useState([]); const [pendingInvites, setPendingInvites] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [showResults, setShowResults] = useState([]); const [loading, setLoading] = useState(true); const [creating, setCreating] = useState(false); const [searchTimeout, setSearchTimeout] = useState(null); const [respondingTo, setRespondingTo] = useState(null); const { socket } = useSocket(); const searchRef = useRef(null); useClickOutside(searchRef, () => setShowResults([])); useEffect(() => { loadChallenges(); }, []); // Listen for real-time challenge invitations useEffect(() => { if (!socket) return; const handleChallengeInvitation = (invitation) => { toast.success(`📬 ${invitation.invited_by} invited you to "${invitation.challenge_title}"`); loadChallenges(); // Refresh to show new invitation }; socket.on('challenge:invitation', handleChallengeInvitation); return () => { socket.off('challenge:invitation', handleChallengeInvitation); }; }, [socket]); const loadChallenges = async () => { try { const data = await api.getChallenges(); setChallenges(data.challenges); // Filter out pending invitations const pending = data.challenges.filter(c => c.participation_status === 'pending'); setPendingInvites(pending); } catch (err) { console.error('Failed to load challenges:', err); } finally { setLoading(false); } }; const handleRespondToInvite = async (challengeId, status) => { setRespondingTo(challengeId); try { await api.respondToChallenge(challengeId, status); toast.success(status === 'accepted' ? 'Challenge accepted!' : 'Challenge declined'); await loadChallenges(); } catch (err) { toast.error('Failed to respond: ' + err.message); } finally { setRespondingTo(null); } }; const handleSearch = (query) => { setSearchQuery(query); // Clear previous timeout if (searchTimeout) { clearTimeout(searchTimeout); } if (query.trim().length < 2) { setShowResults([]); return; } // Debounce search by 1.5 seconds const timeout = setTimeout(async () => { try { const data = await api.searchShows(query); setShowResults(data.results || []); } catch (err) { console.error('Search failed:', err); } }, 1500); setSearchTimeout(timeout); }; const handleCreateChallenge = async (show) => { setCreating(true); try { const coverImage = show.poster_path ? `https://image.tmdb.org/t/p/w500${show.poster_path}` : null; await api.createChallenge({ title: show.title, cover_image_url: coverImage, tmdb_id: show.id, media_type: show.media_type }); toast.success('Challenge created!'); setSearchQuery(''); setShowResults([]); await loadChallenges(); } catch (err) { toast.error('Failed to create challenge: ' + err.message); } finally { setCreating(false); } }; if (loading) { return
No challenges yet. Search for a show above to create one!
Created by {challenge.creator_username}
{challenge.my_points} points