fix: use game state playerMetadata instead of GameModeContext in UI components

Replace useGameMode() calls with state.playerMetadata in PlayerStatusBar and
MemoryGrid to ensure only players in the current game are displayed.

Before: UI components used GameModeContext which includes all room members'
players, causing remote players to appear in local-only games.

After: UI components use state.playerMetadata and state.activePlayers from
MemoryPairsContext, which only contains players actually in the current game.

Changes:
- PlayerStatusBar: Get players from state.playerMetadata, not GameModeContext
- MemoryGrid: Check player.userId === viewerId instead of isLocal flag
- Remove useGameMode imports from display components

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-09 18:07:38 -05:00
parent fa827ac792
commit 388c25451d
2 changed files with 19 additions and 23 deletions

View File

@@ -3,7 +3,7 @@
import { useSpring, animated } from '@react-spring/web'
import { useEffect, useMemo, useRef, useState } from 'react'
import { css } from '../../../../../styled-system/css'
import { useGameMode } from '../../../../contexts/GameModeContext'
import { useViewerId } from '@/hooks/useViewerId'
import { useMemoryPairs } from '../context/MemoryPairsContext'
import { getGridConfiguration } from '../utils/cardGeneration'
import { GameCard } from './GameCard'
@@ -173,7 +173,7 @@ function HoverAvatar({
export function MemoryGrid() {
const { state, flipCard, hoverCard, gameMode } = useMemoryPairs()
const { players: playerMap } = useGameMode()
const { data: viewerId } = useViewerId()
// Track card element refs for positioning hover avatars
const cardRefs = useRef<Map<string, HTMLElement>>(new Map())
@@ -182,9 +182,11 @@ export function MemoryGrid() {
const isMyTurn = useMemo(() => {
if (gameMode === 'single') return true // Always your turn in single player
const currentPlayerData = playerMap.get(state.currentPlayer)
return currentPlayerData?.isLocal === true
}, [state.currentPlayer, playerMap, gameMode])
// In local games, all players belong to current user, so always their turn
// In room games, check if current player belongs to this user
const currentPlayerMetadata = state.playerMetadata?.[state.currentPlayer]
return currentPlayerMetadata?.userId === viewerId
}, [state.currentPlayer, state.playerMetadata, viewerId, gameMode])
// Hooks must be called before early return
const gridConfig = useMemo(() => getGridConfiguration(state.difficulty), [state.difficulty])
@@ -200,16 +202,8 @@ export function MemoryGrid() {
// Get player metadata for hover avatars
const getPlayerHoverInfo = (playerId: string) => {
// Check playerMetadata first (from room members)
if (state.playerMetadata && state.playerMetadata[playerId]) {
return {
emoji: state.playerMetadata[playerId].emoji,
name: state.playerMetadata[playerId].name,
color: state.playerMetadata[playerId].color,
}
}
// Fall back to local player map
const player = playerMap.get(playerId)
// Get player info from game state metadata
const player = state.playerMetadata?.[playerId]
return player
? {
emoji: player.emoji,
@@ -387,8 +381,10 @@ export function MemoryGrid() {
Object.entries(state.playerHovers)
.filter(([playerId]) => {
// Don't show your own hover avatar (only show remote players)
const player = playerMap.get(playerId)
return player?.isLocal !== true
// In local games, all players belong to this user
// In room games, check if player belongs to different user
const player = state.playerMetadata?.[playerId]
return player?.userId !== viewerId
})
.map(([playerId, cardId]) => {
const playerInfo = getPlayerHoverInfo(playerId)

View File

@@ -1,7 +1,6 @@
'use client'
import { css } from '../../../../../styled-system/css'
import { useGameMode } from '../../../../contexts/GameModeContext'
import { gamePlurals } from '../../../../utils/pluralization'
import { useMemoryPairs } from '../context/MemoryPairsContext'
@@ -10,12 +9,12 @@ interface PlayerStatusBarProps {
}
export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
const { players: playerMap, activePlayers: activePlayerIds } = useGameMode()
const { state } = useMemoryPairs()
// Get active players array
const activePlayersData = Array.from(activePlayerIds)
.map((id) => playerMap.get(id))
// Get active players from game state (not GameModeContext)
// This ensures we only show players actually in this game
const activePlayersData = state.activePlayers
.map((id) => state.playerMetadata?.[id])
.filter((p): p is NonNullable<typeof p> => p !== undefined)
// Map active players to display data with scores
@@ -26,7 +25,8 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
displayEmoji: player.emoji,
score: state.scores[player.id] || 0,
consecutiveMatches: state.consecutiveMatches?.[player.id] || 0,
isLocalPlayer: player.isLocal !== false, // Local if not explicitly marked as remote
// In local games all players are local, in room games check metadata
isLocalPlayer: state.gameMode === 'single' || state.gameMode === 'multiplayer',
}))
// Check if current player is local (your turn) or remote (waiting)