import "./styles.sass"; import {SocketContext} from "@/common/contexts/SocketContext"; import {useContext, useState, useEffect, useRef, useCallback} from "react"; import MusicSlider from "@/pages/Game/components/MusicSlider"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import { faMessage, faMusic, faHeadphones, faClock, faCrown, faPaperPlane, faCheckCircle, faVolumeUp, faStepForward } from "@fortawesome/free-solid-svg-icons"; import { fetchPlaylistSongs } from "@/services/youtubeService.js"; import YouTubePlayer from "../../components/YouTubePlayer/YouTubePlayer"; export const Game = () => { const {send, on, socket, connected, connect} = useContext(SocketContext); useEffect(() => { if (!connected) { connect(); } }, [connected, connect]); const [role, setRole] = useState(null); const [round, setRound] = useState(1); const [phase, setPhase] = useState("waiting"); const [timeLeft, setTimeLeft] = useState(30); const [frequency, setFrequency] = useState(440); const [currentSong, setCurrentSong] = useState(null); const [songOptions, setSongOptions] = useState([]); const [scores, setScores] = useState({}); const [selectedSong, setSelectedSong] = useState(null); const [guessResult, setGuessResult] = useState(null); const [isHost, setIsHost] = useState(false); const [hasGuessed, setHasGuessed] = useState(false); const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(""); const [connectedUsers, setConnectedUsers] = useState([]); const [username, setUsername] = useState(""); const messageEndRef = useRef(null); const timerIntervalRef = useRef(null); const [allSongs, setAllSongs] = useState([]); const [songsLoading, setSongsLoading] = useState(false); const [composerIsPlaying, setComposerIsPlaying] = useState(false); useEffect(() => { if (!connected) return; const eventHandlers = { "roles-assigned": (roles) => { const myRole = roles[socket?.id]; if (myRole) { setRole(myRole); setMessages(prev => [...prev, { system: true, text: myRole === "composer" ? "Du bist der Komponist! Spiele den Song mit dem Tonregler." : "Du bist ein Rater! Höre die Frequenzen und versuche, den Song zu erkennen." }]); } }, "song-selected": setCurrentSong, "round-started": (data) => { setRound(data.round); setPhase("composing"); setTimeLeft(data.timeRemaining); }, "guessing-phase-started": (data) => { console.log("Guessing phase started:", data); setPhase("guessing"); setTimeLeft(data.timeRemaining); setSongOptions(data.songOptions || []); }, "guess-result": (result) => { setGuessResult(result.isCorrect); setCurrentSong(result.correctSong); }, "round-results": (results) => { setPhase("results"); const scoresWithNames = {}; Object.entries(results.scores).forEach(([userId, score]) => { const user = connectedUsers.find(u => u.id === userId); scoresWithNames[userId] = { score: score, name: user?.name || results.playerNames?.[userId] || "Player" }; }); setScores(scoresWithNames); if (!currentSong) { setCurrentSong(results.selectedSong); } }, "room-users-update": (users) => { setConnectedUsers(users); const currentUser = users.find(u => u.id === socket?.id); if (currentUser) setUsername(currentUser.name); }, "host-status": (status) => setIsHost(status.isHost), "chat-message": (msg) => setMessages(prev => [...prev, msg]), "user-connected": (userData) => { setMessages(prev => [...prev, { system: true, text: `${userData.name} ist beigetreten` }]); setConnectedUsers(prev => [...prev, userData]); }, "user-disconnected": (userId) => { setConnectedUsers(prev => { const user = prev.find(u => u.id === userId); if (user) { setMessages(prevMsgs => [...prevMsgs, { system: true, text: `${user.name} hat den Raum verlassen` }]); } return prev.filter(u => u.id !== userId); }); }, "frequency-update": (data) => { if (phase === "composing") { console.log("Received frequency update:", data.frequency); setFrequency(data.frequency); setComposerIsPlaying(data.isPlaying); // Make sure isPlaying is handled } }, "phase-changed": (data) => { console.log("Phase changed:", data); setPhase(data.phase); if (data.timeRemaining) { setTimeLeft(data.timeRemaining); } }, "chat-message-confirmation": (msg) => { setMessages(prev => prev.map(prevMsg => { if (!prevMsg.system && prevMsg.text === msg.text && prevMsg.sender !== msg.sender) { console.log(`Updating message sender from "${prevMsg.sender}" to "${msg.sender}"`); return { ...prevMsg, sender: msg.sender }; } return prevMsg; })); }, }; const cleanupFunctions = Object.entries(eventHandlers).map( ([event, handler]) => on(event, handler) ); if (connected) { send("get-room-users"); send("check-host-status"); } return () => cleanupFunctions.forEach(cleanup => cleanup()); }, [socket, on, send, role, currentSong, phase, connected, connectedUsers]); useEffect(() => { if (timerIntervalRef.current) clearInterval(timerIntervalRef.current); if (phase !== "waiting" && phase !== "results") { timerIntervalRef.current = setInterval(() => { setTimeLeft(prev => Math.max(0, prev - 1)); }, 1000); } return () => { if (timerIntervalRef.current) clearInterval(timerIntervalRef.current); }; }, [phase]); useEffect(() => { messageEndRef.current?.scrollIntoView({behavior: "smooth"}); }, [messages]); useEffect(() => { if (!connected || !socket) return; const loadSongs = async () => { try { setSongsLoading(true); console.log("Fetching songs with active socket:", socket?.id); const songs = await fetchPlaylistSongs(socket); console.log(`Successfully loaded ${songs.length} songs`); setAllSongs(songs); } catch (error) { console.error("Error loading songs:", error); } finally { setSongsLoading(false); } }; loadSongs(); }, [socket, connected]); useEffect(() => { if (!allSongs.length || !currentSong || !currentSong.id) return; const enhancedSong = allSongs.find(song => song.id === currentSong.id); if (enhancedSong && enhancedSong !== currentSong) { setCurrentSong(enhancedSong); } }, [allSongs, currentSong]); useEffect(() => { if (!allSongs.length || !songOptions.length) return; const enhancedOptions = songOptions.map(option => { if (option.id && !option.title) { return allSongs.find(song => song.id === option.id) || option; } return option; }); if (JSON.stringify(enhancedOptions) !== JSON.stringify(songOptions)) { setSongOptions(enhancedOptions); } }, [allSongs, songOptions]); const handleFrequencyChange = useCallback((newFrequency, isPlaying) => { setFrequency(newFrequency); setComposerIsPlaying(isPlaying); if (role === "composer") { send("submit-frequency", { frequency: newFrequency, isPlaying }); } }, [role, send]); const handleSendMessage = useCallback(() => { if (inputValue.trim()) { const currentUser = connectedUsers.find(u => u.id === socket?.id); const senderName = currentUser?.name || username || "Player"; const messageData = { text: inputValue, sender: senderName }; send("send-message", messageData); setMessages(prev => [...prev, messageData]); setInputValue(""); } }, [inputValue, send, socket?.id, connectedUsers, username]); const handleSongSelect = useCallback((song) => { setSelectedSong(song); }, []); const handleSubmitGuess = useCallback(() => { if (!selectedSong || phase !== 'guessing') return; console.log("Submitting guess:", selectedSong.id); send("submit-guess", { songId: selectedSong.id }); setMessages(prev => [...prev, { system: true, text: `Du hast "${selectedSong.title}" von ${selectedSong.artist} gewählt.` }]); setHasGuessed(true); }, [selectedSong, send, phase]); const handleNextRound = useCallback(() => { send("next-round"); setSelectedSong(null); setGuessResult(null); setTimeLeft(0); }, [send]); const handlePlayerReady = useCallback(() => { console.log("Player ready"); }, []); const renderPhaseContent = () => { switch (phase) { case "waiting": return (

Warten auf Spielstart...

); case "composing": return (

Runde {round}: {role === "composer" ? "Spielen" : "Zuhören"}

{timeLeft}s
{role === "composer" && currentSong && (
{currentSong.title}
{currentSong.title}
von {currentSong.artist}

Use the player above to listen, and the tone slider to play!

)} {role === "guesser" && (

Höre genau zu und versuche, den Song zu erkennen!

)}
); case "guessing": return (

Runde {round}: Song erraten

{timeLeft}s
{role === "composer" ? (

Die Rater versuchen nun, deinen Song zu erraten...

) : (

Welchen Song hat der Komponist gespielt?

{songOptions.length === 0 ? (

Lade Songoptionen...

) : ( <>
{songOptions.map(song => (
!hasGuessed && handleSongSelect(song)} >
{song.title} {selectedSong?.id === song.id && (
)}
{song.title}
{song.artist}
))}
{hasGuessed ? (

Deine Antwort wurde eingereicht!

) : ( )}
)}
)}
); case "results": return (

Runde {round}: Ergebnisse

{role === "composer" ? (

Die Rater haben versucht, deinen Song zu erraten.

) : (
{currentSong && (

Der richtige Song war:

{currentSong.title}
{currentSong.title}
von {currentSong.artist}
)} {guessResult !== null && (
{guessResult ? 'Richtig! Du erhälst 10 Punkte.' : 'Leider falsch. Kein Punkt.'}
)}
)}

Punktestand

{Object.entries(scores).map(([userId, scoreData]) => { const isCurrentUser = userId === socket?.id; const isHost = connectedUsers.find(u => u.id === "user1")?.id === userId; const playerName = typeof scoreData === 'object' ? scoreData.name : (connectedUsers.find(u => u.id === userId)?.name || "Player"); const playerScore = typeof scoreData === 'object' ? scoreData.score : scoreData; return (
{isCurrentUser && "👉 "} {playerName} {isHost && ( )} {playerScore}
); })}
{isHost ? ( ) : (

Warten auf Rundenwechsel durch Host...

)}
); default: return
Unknown phase
; } }; return (
{role === "composer" ? "Komponist" : "Rater"}

ToneGuessr

Runde {round}
{renderPhaseContent()}
Chat
{messages.map((message, index) => (
{message.system ? ( {message.text} ) : ( <> {message.sender}: {message.text} )}
))}
setInputValue(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()} placeholder="Nachricht..." />
{phase === "composing" && ( )} {songsLoading && (
Loading songs...
)}
); }; export default Game;