'use client'; import { useEffect, useState } from 'react'; import AudioPlayer from './AudioPlayer'; import GuessInput from './GuessInput'; import Statistics from './Statistics'; import { useGameState } from '../lib/gameState'; import { sendGotifyNotification } from '../app/actions'; interface GameProps { dailyPuzzle: { id: number; audioUrl: string; songId: number; title: string; artist: string; coverImage: string | null; } | null; genre?: string | null; } const UNLOCK_STEPS = [2, 4, 7, 11, 16, 30, 60]; export default function Game({ dailyPuzzle, genre = null }: GameProps) { const { gameState, statistics, addGuess } = useGameState(genre); const [hasWon, setHasWon] = useState(false); const [hasLost, setHasLost] = useState(false); const [shareText, setShareText] = useState('Share Result'); const [lastAction, setLastAction] = useState<'GUESS' | 'SKIP' | null>(null); useEffect(() => { if (gameState && dailyPuzzle) { setHasWon(gameState.isSolved); setHasLost(gameState.isFailed); } }, [gameState, dailyPuzzle]); useEffect(() => { setLastAction(null); }, [dailyPuzzle?.id]); if (!dailyPuzzle) return (

No Puzzle Available

Could not generate a daily puzzle.

Please ensure there are songs in the database{genre ? ` for genre "${genre}"` : ''}.

Go to Admin Dashboard
); if (!gameState) return
Loading state...
; const handleGuess = (song: any) => { setLastAction('GUESS'); if (song.id === dailyPuzzle.songId) { addGuess(song.title, true); setHasWon(true); sendGotifyNotification(gameState.guesses.length + 1, 'won', dailyPuzzle.id); } else { addGuess(song.title, false); if (gameState.guesses.length + 1 >= 7) { setHasLost(true); setHasWon(false); // Ensure won is false sendGotifyNotification(7, 'lost', dailyPuzzle.id); } } }; const handleSkip = () => { setLastAction('SKIP'); addGuess("SKIPPED", false); }; const handleGiveUp = () => { setLastAction('SKIP'); addGuess("SKIPPED", false); setHasLost(true); setHasWon(false); sendGotifyNotification(7, 'lost', dailyPuzzle.id); }; const unlockedSeconds = UNLOCK_STEPS[Math.min(gameState.guesses.length, 6)]; const handleShare = () => { let emojiGrid = ''; const totalGuesses = 7; // Build the grid for (let i = 0; i < totalGuesses; i++) { if (i < gameState.guesses.length) { // If this was the winning guess (last one and won) if (hasWon && i === gameState.guesses.length - 1) { emojiGrid += '🟩'; } else { // Wrong or skipped emojiGrid += '⬛'; } } else { // Unused attempts emojiGrid += '⬜'; } } const speaker = hasWon ? '🔉' : '🔇'; const genreText = genre ? `Genre: ${genre}\n` : ''; const text = `Hördle #${dailyPuzzle.id}\n${genreText}\n${speaker}${emojiGrid}\n\n#Hördle #Music\n\nhttps://hoerdle.elpatron.me`; // Fallback method for copying to clipboard const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); setShareText('Copied!'); setTimeout(() => setShareText('Share Result'), 2000); } catch (err) { console.error('Failed to copy:', err); setShareText('Copy failed'); setTimeout(() => setShareText('Share Result'), 2000); } finally { document.body.removeChild(textarea); } }; return (

Hördle #{dailyPuzzle.id}

Attempt {gameState.guesses.length + 1} / 7 {unlockedSeconds}s unlocked
{gameState.guesses.map((guess, i) => { const isCorrect = hasWon && i === gameState.guesses.length - 1; return (
#{i + 1} {isCorrect ? 'Correct!' : guess}
); })}
{!hasWon && !hasLost && ( <> {gameState.guesses.length < 6 ? ( ) : ( )} )} {hasWon && (

You won!

Come back tomorrow for a new song.

{/* Song Details */}
{dailyPuzzle.coverImage && ( Album Cover )}

{dailyPuzzle.title}

{dailyPuzzle.artist}

{statistics && }
)} {hasLost && (

Game Over

The song was:

{/* Song Details */}
{dailyPuzzle.coverImage && ( Album Cover )}

{dailyPuzzle.title}

{dailyPuzzle.artist}

{statistics && }
)}
); }