feat(memory-quiz): persist game settings per-game across sessions
Implement per-game settings persistence so that when users switch between games and come back, their settings are restored. Settings are saved to the room's gameConfig field in the database. Changes: - Add useUpdateGameConfig hook to save settings to room - Load settings from roomData.gameConfig on provider initialization - Merge saved config with initialState using useMemo - Save settings to database when setConfig is called - Settings persist across: - Game switches (memory-quiz -> matching -> memory-quiz) - Page refreshes - New arcade sessions Settings saved: selectedCount, displayTime, selectedDifficulty, playMode 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useGameMode } from '@/contexts/GameModeContext'
|
||||
import { useArcadeSession } from '@/hooks/useArcadeSession'
|
||||
import { useRoomData } from '@/hooks/useRoomData'
|
||||
import { useRoomData, useUpdateGameConfig } from '@/hooks/useRoomData'
|
||||
import { useViewerId } from '@/hooks/useViewerId'
|
||||
import type { GameMove } from '@/lib/arcade/validation'
|
||||
import { TEAM_MOVE } from '@/lib/arcade/validation/types'
|
||||
@@ -238,6 +238,7 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
|
||||
const { data: viewerId } = useViewerId()
|
||||
const { roomData } = useRoomData()
|
||||
const { activePlayers: activePlayerIds, players } = useGameMode()
|
||||
const { mutate: updateGameConfig } = useUpdateGameConfig()
|
||||
|
||||
// Get active player IDs as array
|
||||
const activePlayers = Array.from(activePlayerIds)
|
||||
@@ -246,6 +247,23 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
|
||||
// This prevents sending a network request for every keystroke
|
||||
const [localCurrentInput, setLocalCurrentInput] = useState('')
|
||||
|
||||
// Merge saved game config from room with initialState
|
||||
const mergedInitialState = useMemo(() => {
|
||||
const savedConfig = roomData?.gameConfig as Record<string, any> | null | undefined
|
||||
if (!savedConfig) return initialState
|
||||
|
||||
console.log('[RoomMemoryQuizProvider] Loading saved game config:', savedConfig)
|
||||
|
||||
return {
|
||||
...initialState,
|
||||
// Restore settings from saved config
|
||||
selectedCount: savedConfig.selectedCount ?? initialState.selectedCount,
|
||||
displayTime: savedConfig.displayTime ?? initialState.displayTime,
|
||||
selectedDifficulty: savedConfig.selectedDifficulty ?? initialState.selectedDifficulty,
|
||||
playMode: savedConfig.playMode ?? initialState.playMode,
|
||||
}
|
||||
}, [roomData?.gameConfig])
|
||||
|
||||
// Arcade session integration WITH room sync
|
||||
const {
|
||||
state,
|
||||
@@ -255,7 +273,7 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
|
||||
} = useArcadeSession<SorobanQuizState>({
|
||||
userId: viewerId || '',
|
||||
roomId: roomData?.id, // CRITICAL: Pass roomId for network sync across room members
|
||||
initialState,
|
||||
initialState: mergedInitialState,
|
||||
applyMove: applyMoveOptimistically,
|
||||
})
|
||||
|
||||
@@ -421,8 +439,21 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
|
||||
userId: viewerId || '',
|
||||
data: { field, value },
|
||||
})
|
||||
|
||||
// Save setting to room's gameConfig for persistence
|
||||
if (roomData?.id) {
|
||||
const updatedConfig = {
|
||||
...(roomData.gameConfig as Record<string, any>),
|
||||
[field]: value,
|
||||
}
|
||||
console.log('[RoomMemoryQuizProvider] Saving game config:', updatedConfig)
|
||||
updateGameConfig({
|
||||
roomId: roomData.id,
|
||||
gameConfig: updatedConfig,
|
||||
})
|
||||
}
|
||||
},
|
||||
[viewerId, sendMove]
|
||||
[viewerId, sendMove, roomData?.id, roomData?.gameConfig, updateGameConfig]
|
||||
)
|
||||
|
||||
// Merge network state with local input state
|
||||
|
||||
@@ -665,3 +665,34 @@ export function useClearRoomGame() {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update game config for current room (game-specific settings)
|
||||
*/
|
||||
async function updateGameConfigApi(params: {
|
||||
roomId: string
|
||||
gameConfig: Record<string, unknown>
|
||||
}): Promise<void> {
|
||||
const response = await fetch(`/api/arcade/rooms/${params.roomId}/settings`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
gameConfig: params.gameConfig,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
throw new Error(errorData.error || 'Failed to update game config')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook: Update game config for current room
|
||||
* This allows games to persist their settings (e.g., difficulty, card count)
|
||||
*/
|
||||
export function useUpdateGameConfig() {
|
||||
return useMutation({
|
||||
mutationFn: updateGameConfigApi,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user