Files
Trivy/frontend/src/stores/gameStore.ts
consultoria-as 112f489e40 feat: reconexión de sesión + 6 nuevas categorías + corrección de bugs
- Añade sistema de reconexión tras refresh/cierre del navegador
  - Persistencia de sesión en localStorage (3h TTL)
  - Banner de reconexión en Home
  - Evento rejoin_room en backend

- Nuevas categorías: Series TV, Marvel/DC, Disney, Memes, Pokémon, Mitología

- Correcciones de bugs:
  - Fix: juego bloqueado al fallar robo (steal decision)
  - Fix: jugador duplicado al cambiar de equipo
  - Fix: rotación incorrecta de turno tras fallo

- Config: soporte para Cloudflare tunnel (allowedHosts)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 01:53:32 +00:00

215 lines
5.1 KiB
TypeScript

import { create } from 'zustand'
import type { GameRoom, Question, ChatMessage, Achievement } from '../types'
// Session persistence helpers
const SESSION_KEY = 'trivy_session'
interface SavedSession {
roomCode: string
playerName: string
team: 'A' | 'B'
timestamp: number
}
export function saveSession(roomCode: string, playerName: string, team: 'A' | 'B') {
const session: SavedSession = {
roomCode,
playerName,
team,
timestamp: Date.now()
}
localStorage.setItem(SESSION_KEY, JSON.stringify(session))
}
export function getSavedSession(): SavedSession | null {
try {
const data = localStorage.getItem(SESSION_KEY)
if (!data) return null
const session: SavedSession = JSON.parse(data)
// Session expires after 3 hours (same as room TTL)
const threeHours = 3 * 60 * 60 * 1000
if (Date.now() - session.timestamp > threeHours) {
clearSession()
return null
}
return session
} catch {
return null
}
}
export function clearSession() {
localStorage.removeItem(SESSION_KEY)
}
export interface Reaction {
id: string
player_name: string
team: 'A' | 'B'
emoji: string
timestamp: string
}
export interface TeamMessage {
player_name: string
team: 'A' | 'B'
message: string
timestamp: string
}
const MAX_TEAM_MESSAGES = 50
interface GameState {
// Room state
room: GameRoom | null
setRoom: (room: GameRoom | null) => void
// Player info
playerName: string
setPlayerName: (name: string) => void
// Current question
currentQuestion: Question | null
setCurrentQuestion: (question: Question | null) => void
// Timer
timerEnd: Date | null
setTimerEnd: (end: Date | null) => void
// Chat messages
messages: ChatMessage[]
addMessage: (message: ChatMessage) => void
clearMessages: () => void
// Team chat messages
teamMessages: TeamMessage[]
addTeamMessage: (message: TeamMessage) => void
clearTeamMessages: () => void
// Achievements
achievements: Achievement[]
setAchievements: (achievements: Achievement[]) => void
unlockAchievement: (id: number) => void
// Game stats (for achievements tracking)
stats: {
correctStreak: number
stealsAttempted: number
stealsSuccessful: number
categoryCorrect: Record<number, number>
fastAnswers: number
maxDeficit: number
}
updateStats: (updates: Partial<GameState['stats']>) => void
resetStats: () => void
// UI state
showStealPrompt: boolean
setShowStealPrompt: (show: boolean) => void
// Reactions
reactions: Reaction[]
addReaction: (reaction: Omit<Reaction, 'id'>) => void
removeReaction: (id: string) => void
clearReactions: () => void
// Game result
gameResult: {
winner: 'A' | 'B' | null
finalScores: { A: number; B: number }
replayCode: string | null
achievementsUnlocked: Array<{
player_name: string
team: 'A' | 'B'
achievement: Achievement
}>
} | null
setGameResult: (result: GameState['gameResult']) => void
// Reset
resetGame: () => void
}
const initialStats = {
correctStreak: 0,
stealsAttempted: 0,
stealsSuccessful: 0,
categoryCorrect: {},
fastAnswers: 0,
maxDeficit: 0,
}
export const useGameStore = create<GameState>((set) => ({
room: null,
setRoom: (room) => set({ room }),
playerName: '',
setPlayerName: (playerName) => set({ playerName }),
currentQuestion: null,
setCurrentQuestion: (currentQuestion) => set({ currentQuestion }),
timerEnd: null,
setTimerEnd: (timerEnd) => set({ timerEnd }),
messages: [],
addMessage: (message) =>
set((state) => ({ messages: [...state.messages, message].slice(-100) })),
clearMessages: () => set({ messages: [] }),
teamMessages: [],
addTeamMessage: (message) =>
set((state) => ({
teamMessages: [...state.teamMessages, message].slice(-MAX_TEAM_MESSAGES),
})),
clearTeamMessages: () => set({ teamMessages: [] }),
achievements: [],
setAchievements: (achievements) => set({ achievements }),
unlockAchievement: (id) =>
set((state) => ({
achievements: state.achievements.map((a) =>
a.id === id ? { ...a, unlocked: true, unlockedAt: new Date().toISOString() } : a
),
})),
stats: initialStats,
updateStats: (updates) =>
set((state) => ({ stats: { ...state.stats, ...updates } })),
resetStats: () => set({ stats: initialStats }),
showStealPrompt: false,
setShowStealPrompt: (showStealPrompt) => set({ showStealPrompt }),
reactions: [],
addReaction: (reaction) =>
set((state) => ({
reactions: [
...state.reactions,
{ ...reaction, id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` },
],
})),
removeReaction: (id) =>
set((state) => ({
reactions: state.reactions.filter((r) => r.id !== id),
})),
clearReactions: () => set({ reactions: [] }),
gameResult: null,
setGameResult: (gameResult) => set({ gameResult }),
resetGame: () =>
set({
room: null,
currentQuestion: null,
timerEnd: null,
messages: [],
teamMessages: [],
reactions: [],
stats: initialStats,
showStealPrompt: false,
gameResult: null,
}),
}))