Compare commits

...

4 Commits

Author SHA1 Message Date
semantic-release-bot
74554c3669 chore(release): 3.17.7 [skip ci]
## [3.17.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.6...v3.17.7) (2025-10-15)

### Bug Fixes

* **arcade:** prevent gameConfig from being overwritten when switching games ([a89d3a9](a89d3a9701))
2025-10-15 17:33:21 +00:00
Thomas Hallock
a89d3a9701 fix(arcade): prevent gameConfig from being overwritten when switching games
Root cause: setRoomGameApi was sending `gameConfig: {}` when gameConfig
was undefined, which overwrote all saved settings in the database.

Changes:
- Client: Only include gameConfig in request body if explicitly provided
- Server: Only include gameConfig in socket broadcast if provided
- Client handler: Update gameConfig from broadcast if present

This preserves all game settings (difficulty, card count, etc.) when
switching between games in a room.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 12:32:31 -05:00
semantic-release-bot
180e213d00 chore(release): 3.17.6 [skip ci]
## [3.17.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.5...v3.17.6) (2025-10-15)

### Code Refactoring

* **logging:** use JSON.stringify for all object logging ([c33698c](c33698ce52))
2025-10-15 17:30:09 +00:00
Thomas Hallock
c33698ce52 refactor(logging): use JSON.stringify for all object logging
Replace collapsed object logging with JSON.stringify to ensure full
object details are visible when console logs are copied/pasted.

This affects all settings persistence logging:
- Loading settings from database
- Saving settings to database
- API calls to server
- Cache updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 12:29:08 -05:00
5 changed files with 118 additions and 37 deletions

View File

@@ -1,3 +1,17 @@
## [3.17.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.6...v3.17.7) (2025-10-15)
### Bug Fixes
* **arcade:** prevent gameConfig from being overwritten when switching games ([a89d3a9](https://github.com/antialias/soroban-abacus-flashcards/commit/a89d3a970137471e2652de992c45370dbb97416d))
## [3.17.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.5...v3.17.6) (2025-10-15)
### Code Refactoring
* **logging:** use JSON.stringify for all object logging ([c33698c](https://github.com/antialias/soroban-abacus-flashcards/commit/c33698ce52ebdc18ce3a0d856f9241c7389ed651))
## [3.17.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.4...v3.17.5) (2025-10-15)

View File

@@ -117,11 +117,21 @@ export async function PATCH(req: NextRequest, context: RouteContext) {
if (io) {
try {
console.log(`[Settings API] Broadcasting game change to room ${roomId}: ${body.gameName}`)
io.to(`room:${roomId}`).emit('room-game-changed', {
const broadcastData: {
roomId: string
gameName: string | null
gameConfig?: Record<string, unknown>
} = {
roomId,
gameName: body.gameName,
gameConfig: body.gameConfig || {},
})
}
// Only include gameConfig if it was explicitly provided
if (body.gameConfig !== undefined) {
broadcastData.gameConfig = body.gameConfig
}
io.to(`room:${roomId}`).emit('room-game-changed', broadcastData)
} catch (socketError) {
console.error('[Settings API] Failed to broadcast game change:', socketError)
}

View File

@@ -252,10 +252,17 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
// Settings are scoped by game name to preserve settings when switching games
const mergedInitialState = useMemo(() => {
const gameConfig = roomData?.gameConfig as Record<string, any> | null | undefined
console.log('[RoomMemoryPairsProvider] Loading settings from database:', {
gameConfig,
roomId: roomData?.id,
})
console.log(
'[RoomMemoryPairsProvider] Loading settings from database:',
JSON.stringify(
{
gameConfig,
roomId: roomData?.id,
},
null,
2
)
)
if (!gameConfig) {
console.log('[RoomMemoryPairsProvider] No gameConfig, using initialState')
@@ -264,7 +271,10 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
// Get settings for this specific game (matching)
const savedConfig = gameConfig.matching as Record<string, any> | null | undefined
console.log('[RoomMemoryPairsProvider] Saved config for matching:', savedConfig)
console.log(
'[RoomMemoryPairsProvider] Saved config for matching:',
JSON.stringify(savedConfig, null, 2)
)
if (!savedConfig) {
console.log('[RoomMemoryPairsProvider] No saved config for matching, using initialState')
@@ -278,11 +288,18 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
difficulty: savedConfig.difficulty ?? initialState.difficulty,
turnTimer: savedConfig.turnTimer ?? initialState.turnTimer,
}
console.log('[RoomMemoryPairsProvider] Merged state:', {
gameType: merged.gameType,
difficulty: merged.difficulty,
turnTimer: merged.turnTimer,
})
console.log(
'[RoomMemoryPairsProvider] Merged state:',
JSON.stringify(
{
gameType: merged.gameType,
difficulty: merged.difficulty,
turnTimer: merged.turnTimer,
},
null,
2
)
)
return merged
}, [roomData?.gameConfig])
@@ -558,10 +575,17 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
gameType,
},
}
console.log('[RoomMemoryPairsProvider] Saving gameType to database:', {
roomId: roomData.id,
updatedConfig,
})
console.log(
'[RoomMemoryPairsProvider] Saving gameType to database:',
JSON.stringify(
{
roomId: roomData.id,
updatedConfig,
},
null,
2
)
)
updateGameConfig({
roomId: roomData.id,
gameConfig: updatedConfig,
@@ -597,10 +621,17 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
difficulty,
},
}
console.log('[RoomMemoryPairsProvider] Saving difficulty to database:', {
roomId: roomData.id,
updatedConfig,
})
console.log(
'[RoomMemoryPairsProvider] Saving difficulty to database:',
JSON.stringify(
{
roomId: roomData.id,
updatedConfig,
},
null,
2
)
)
updateGameConfig({
roomId: roomData.id,
gameConfig: updatedConfig,
@@ -636,10 +667,17 @@ export function RoomMemoryPairsProvider({ children }: { children: ReactNode }) {
turnTimer,
},
}
console.log('[RoomMemoryPairsProvider] Saving turnTimer to database:', {
roomId: roomData.id,
updatedConfig,
})
console.log(
'[RoomMemoryPairsProvider] Saving turnTimer to database:',
JSON.stringify(
{
roomId: roomData.id,
updatedConfig,
},
null,
2
)
)
updateGameConfig({
roomId: roomData.id,
gameConfig: updatedConfig,

View File

@@ -449,7 +449,7 @@ export function useRoomData() {
const handleRoomGameChanged = (data: {
roomId: string
gameName: string | null
gameConfig: Record<string, unknown>
gameConfig?: Record<string, unknown>
}) => {
console.log('[useRoomData] Room game changed:', data)
if (data.roomId === roomData?.id) {
@@ -458,6 +458,8 @@ export function useRoomData() {
return {
...prev,
gameName: data.gameName,
// Only update gameConfig if it was provided in the broadcast
...(data.gameConfig !== undefined ? { gameConfig: data.gameConfig } : {}),
}
})
}
@@ -585,13 +587,20 @@ async function setRoomGameApi(params: {
gameName: string
gameConfig?: Record<string, unknown>
}): Promise<void> {
// Only include gameConfig in the request if it was explicitly provided
// Otherwise, we preserve the existing gameConfig in the database
const body: { gameName: string; gameConfig?: Record<string, unknown> } = {
gameName: params.gameName,
}
if (params.gameConfig !== undefined) {
body.gameConfig = params.gameConfig
}
const response = await fetch(`/api/arcade/rooms/${params.roomId}/settings`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
gameName: params.gameName,
gameConfig: params.gameConfig || {},
}),
body: JSON.stringify(body),
})
if (!response.ok) {
@@ -672,10 +681,17 @@ async function updateGameConfigApi(params: {
roomId: string
gameConfig: Record<string, unknown>
}): Promise<void> {
console.log('[updateGameConfigApi] Sending PATCH to server:', {
url: `/api/arcade/rooms/${params.roomId}/settings`,
gameConfig: params.gameConfig,
})
console.log(
'[updateGameConfigApi] Sending PATCH to server:',
JSON.stringify(
{
url: `/api/arcade/rooms/${params.roomId}/settings`,
gameConfig: params.gameConfig,
},
null,
2
)
)
const response = await fetch(`/api/arcade/rooms/${params.roomId}/settings`, {
method: 'PATCH',
@@ -687,7 +703,7 @@ async function updateGameConfigApi(params: {
if (!response.ok) {
const errorData = await response.json()
console.error('[updateGameConfigApi] Server error:', errorData)
console.error('[updateGameConfigApi] Server error:', JSON.stringify(errorData, null, 2))
throw new Error(errorData.error || 'Failed to update game config')
}
@@ -712,7 +728,10 @@ export function useUpdateGameConfig() {
gameConfig: variables.gameConfig,
}
})
console.log('[useUpdateGameConfig] Updated cache with new gameConfig:', variables.gameConfig)
console.log(
'[useUpdateGameConfig] Updated cache with new gameConfig:',
JSON.stringify(variables.gameConfig, null, 2)
)
},
})
}

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "3.17.5",
"version": "3.17.7",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [