import React, { useState, useEffect, useRef } from 'react'; import toast from 'react-hot-toast'; import api from '../api'; import { useClickOutside } from '../hooks/useClickOutside'; import { useSocket } from '../SocketContext'; export default function Friends() { const [friends, setFriends] = useState([]); const [challengeFriends, setChallengeFriends] = useState([]); const [requests, setRequests] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [loading, setLoading] = useState(true); const [searchTimeout, setSearchTimeout] = useState(null); const [responding, setResponding] = useState(null); const [sending, setSending] = useState(null); const { socket } = useSocket(); const searchRef = useRef(null); useClickOutside(searchRef, () => setSearchResults([])); useEffect(() => { loadData(); }, []); // Listen for real-time friend request events useEffect(() => { if (!socket) return; const handleFriendRequest = (request) => { toast.success(`👋 ${request.from_username} sent you a friend request`); loadData(); // Refresh to show new request }; const handleFriendResponse = (response) => { if (response.status === 'accepted') { toast.success(`🎉 ${response.friend_username} accepted your friend request!`); loadData(); // Refresh friends list } else { toast(`${response.friend_username} declined your friend request`); } }; socket.on('friend:request', handleFriendRequest); socket.on('friend:response', handleFriendResponse); return () => { socket.off('friend:request', handleFriendRequest); socket.off('friend:response', handleFriendResponse); }; }, [socket]); const loadData = async () => { try { const [friendsData, requestsData] = await Promise.all([ api.getFriends(), api.getFriendRequests() ]); setFriends(friendsData.friends); setChallengeFriends(friendsData.challenge_friends || []); setRequests(requestsData.requests); } catch (err) { console.error('Failed to load data:', err); } finally { setLoading(false); } }; const handleSearch = (query) => { setSearchQuery(query); // Clear previous timeout if (searchTimeout) { clearTimeout(searchTimeout); } if (query.trim().length < 2) { setSearchResults([]); return; } // Debounce search by 1 second const timeout = setTimeout(async () => { try { const data = await api.searchUsers(query); setSearchResults(data.users); } catch (err) { console.error('Search failed:', err); } }, 1000); setSearchTimeout(timeout); }; const handleSendRequest = async (userId) => { setSending(userId); try { await api.sendFriendRequest(userId); setSearchQuery(''); setSearchResults([]); toast.success('Friend request sent!'); } catch (err) { toast.error('Failed to send request: ' + err.message); } finally { setSending(null); } }; const handleRespond = async (requestId, status) => { setResponding(requestId); try { await api.respondToFriendRequest(requestId, status); toast.success(status === 'accepted' ? 'Friend request accepted!' : 'Friend request declined'); await loadData(); } catch (err) { toast.error('Failed to respond: ' + err.message); } finally { setResponding(null); } }; if (loading) { return
Loading...
; } return (

Friends

{/* Search */}

Add Friends

handleSearch(e.target.value)} /> {searchResults.length > 0 && (
{searchResults.map(user => (
{user.username}
{user.email}
))}
)}
{/* Pending Requests */} {requests.length > 0 && (

Friend Requests

{requests.map(req => (
{req.username}
{req.email}
))}
)} {/* Friends List */}

Your Friends

{friends.length === 0 && challengeFriends.length === 0 ? (

No friends yet. Search above to add some!

) : (
{friends.map(friend => (
{friend.username}
{friend.email}
{friend.total_points} points
))} {challengeFriends.length > 0 && ( <>
From Challenges
{challengeFriends.map(friend => (
{friend.username}
{friend.email}
{friend.total_points} points
))} )}
)}
); }