From e0106502b1cd60e9845e613194f4a3389c0de577 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Tue, 27 Jan 2026 02:07:03 +0000 Subject: [PATCH] fix: persistencia de resultados del juego MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Guarda gameResult en localStorage al terminar partida - Results.tsx recupera resultados de localStorage o del room - Expira después de 1 hora - Resuelve "No hay resultados disponibles" tras recargar Co-Authored-By: Claude Opus 4.5 --- frontend/src/hooks/useSocket.ts | 12 ++++- frontend/src/pages/Results.tsx | 83 ++++++++++++++++++++++++-------- frontend/src/stores/gameStore.ts | 49 +++++++++++++++++++ 3 files changed, 122 insertions(+), 22 deletions(-) diff --git a/frontend/src/hooks/useSocket.ts b/frontend/src/hooks/useSocket.ts index bc5c03c..787032b 100644 --- a/frontend/src/hooks/useSocket.ts +++ b/frontend/src/hooks/useSocket.ts @@ -1,5 +1,5 @@ import { useEffect, useCallback } from 'react' -import { useGameStore, saveSession, clearSession } from '../stores/gameStore' +import { useGameStore, saveSession, clearSession, saveGameResult } from '../stores/gameStore' import { soundPlayer } from './useSound' import { useThemeStore } from '../stores/themeStore' import { useSoundStore } from '../stores/soundStore' @@ -193,7 +193,7 @@ export function useSocket() { soundPlayer.play('defeat', volume) } - setGameResult({ + const gameResultData = { winner: data.winner, finalScores: data.final_scores, replayCode: data.replay_code, @@ -202,6 +202,14 @@ export function useSocket() { team: a.team, achievement: a.achievement as Achievement })) + } + + setGameResult(gameResultData) + + // Persist game result to localStorage + saveGameResult({ + ...gameResultData, + roomCode: data.room.code }) }) diff --git a/frontend/src/pages/Results.tsx b/frontend/src/pages/Results.tsx index 8dad459..c0d0d46 100644 --- a/frontend/src/pages/Results.tsx +++ b/frontend/src/pages/Results.tsx @@ -1,33 +1,75 @@ -import { useEffect } from 'react' -import { useNavigate } from 'react-router-dom' +import { useEffect, useMemo } from 'react' +import { useNavigate, useParams } from 'react-router-dom' import { motion } from 'framer-motion' import { useSound } from '../hooks/useSound' -import { useGameStore } from '../stores/gameStore' +import { useGameStore, getSavedGameResult, clearGameResult } from '../stores/gameStore' import { useThemeStyles } from '../themes/ThemeProvider' export default function Results() { const navigate = useNavigate() + const { roomCode } = useParams<{ roomCode: string }>() const { play } = useSound() - const { gameResult, resetGame, playerName, room } = useGameStore() + const { gameResult, resetGame, playerName, room, setGameResult } = useGameStore() const { config, styles } = useThemeStyles() + // Try to recover game result from localStorage if not in store + const effectiveGameResult = useMemo(() => { + if (gameResult) return gameResult + + // Try localStorage + const saved = getSavedGameResult(roomCode) + if (saved) { + return { + winner: saved.winner, + finalScores: saved.finalScores, + replayCode: saved.replayCode, + achievementsUnlocked: saved.achievementsUnlocked + } + } + + // Fallback: use room data if available and game is finished + if (room && room.status === 'finished') { + const teamAScore = room.scores?.A ?? 0 + const teamBScore = room.scores?.B ?? 0 + let winner: 'A' | 'B' | null = null + if (teamAScore > teamBScore) winner = 'A' + else if (teamBScore > teamAScore) winner = 'B' + + return { + winner, + finalScores: { A: teamAScore, B: teamBScore }, + replayCode: null, + achievementsUnlocked: [] + } + } + + return null + }, [gameResult, roomCode, room]) + + // Restore game result to store if recovered from localStorage + useEffect(() => { + if (!gameResult && effectiveGameResult) { + setGameResult(effectiveGameResult) + } + }, [gameResult, effectiveGameResult, setGameResult]) + // Determine if current player won const myTeam = room?.teams.A.find(p => p.name === playerName) ? 'A' : 'B' - const won = gameResult?.winner === myTeam - const tied = gameResult?.winner === null + const won = effectiveGameResult?.winner === myTeam + const tied = effectiveGameResult?.winner === null // Play victory/defeat sound useEffect(() => { - if (gameResult) { + if (effectiveGameResult) { if (won) { play('victory') } else if (!tied) { play('defeat') } } - }, [gameResult, won, tied, play]) + }, [effectiveGameResult, won, tied, play]) - if (!gameResult) { + if (!effectiveGameResult) { return (

No hay resultados disponibles

@@ -36,6 +78,7 @@ export default function Results() { } const handlePlayAgain = () => { + clearGameResult() resetGame() navigate('/') } @@ -54,15 +97,15 @@ export default function Results() { transition={{ type: 'spring', bounce: 0.5 }} className="mb-8" > - {gameResult.winner ? ( + {effectiveGameResult.winner ? (

- ¡Equipo {gameResult.winner} Gana! + ¡Equipo {effectiveGameResult.winner} Gana!

) : (

Equipo A
- {gameResult.finalScores.A} + {effectiveGameResult.finalScores.A}
@@ -101,7 +144,7 @@ export default function Results() { initial={{ x: 50, opacity: 0 }} animate={{ x: 0, opacity: 1 }} transition={{ delay: 0.2 }} - className={`p-6 rounded-lg text-center ${gameResult.winner === 'B' ? 'ring-4' : ''}`} + className={`p-6 rounded-lg text-center ${effectiveGameResult.winner === 'B' ? 'ring-4' : ''}`} style={{ backgroundColor: config.colors.secondary + '20', border: `2px solid ${config.colors.secondary}`, @@ -110,19 +153,19 @@ export default function Results() { >
Equipo B
- {gameResult.finalScores.B} + {effectiveGameResult.finalScores.B}

{/* Achievements Unlocked */} - {gameResult.achievementsUnlocked && gameResult.achievementsUnlocked.length > 0 && ( + {effectiveGameResult.achievementsUnlocked && effectiveGameResult.achievementsUnlocked.length > 0 && (

Logros Desbloqueados

- {gameResult.achievementsUnlocked.map((unlock, i) => ( + {effectiveGameResult.achievementsUnlocked.map((unlock, i) => ( - {gameResult.replayCode && ( + {effectiveGameResult.replayCode && (