Compare commits

...

4 Commits

Author SHA1 Message Date
semantic-release-bot
6b890b30f4 chore(release): 2.17.1 [skip ci]
## [2.17.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.0...v2.17.1) (2025-10-10)

### Bug Fixes

* correct hover avatar and turn indicator to show only current player ([0596ef6](0596ef6587))
2025-10-10 13:31:44 +00:00
Thomas Hallock
0596ef6587 fix: correct hover avatar and turn indicator to show only current player
Previously, hover avatars were showing for remote players while the
current player's avatar was hidden. Also, the turn indicator was
incorrectly showing "Your turn" for all players regardless of whether
they belonged to the current viewer.

Changes:
- MemoryGrid: Filter hover avatars to show only for current player
  (playerId === state.currentPlayer) instead of remote players
- PlayerStatusBar: Check player ownership by comparing player.userId
  with viewerId instead of hardcoded gameMode check

This ensures:
1. Only the current player (whose turn it is) displays their hover avatar
2. Turn indicator correctly shows "Your turn" vs "Their turn" based on
   whether the current player belongs to the local viewer

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 08:30:58 -05:00
semantic-release-bot
debf786ed9 chore(release): 2.17.0 [skip ci]
## [2.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.16.7...v2.17.0) (2025-10-10)

### Features

* hide hover avatar when card is flipped to reveal value ([a2aada2](a2aada2e69))
2025-10-10 13:18:37 +00:00
Thomas Hallock
a2aada2e69 feat: hide hover avatar when card is flipped to reveal value
Avatar now fades out when the card it's hovering over is flipped, ensuring
all users can clearly see revealed card values.

Changes:
- Add isCardFlipped prop to HoverAvatar component
- Check if hovered card is in flippedCards array or matched
- Update opacity calculation to hide avatar when card is flipped
- Avatar smoothly fades out via react-spring when card reveals

This ensures remote players' consideration doesn't obscure card values
during gameplay.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 08:17:37 -05:00
4 changed files with 35 additions and 12 deletions

View File

@@ -1,3 +1,17 @@
## [2.17.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.0...v2.17.1) (2025-10-10)
### Bug Fixes
* correct hover avatar and turn indicator to show only current player ([0596ef6](https://github.com/antialias/soroban-abacus-flashcards/commit/0596ef65879a303f1f71863ef307af69bf270c70))
## [2.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.16.7...v2.17.0) (2025-10-10)
### Features
* hide hover avatar when card is flipped to reveal value ([a2aada2](https://github.com/antialias/soroban-abacus-flashcards/commit/a2aada2e6922fb3af363e0d191275e06b8f8f040))
## [2.16.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.16.6...v2.16.7) (2025-10-10)

View File

@@ -1,9 +1,9 @@
'use client'
import { useSpring, animated } from '@react-spring/web'
import { animated, useSpring } from '@react-spring/web'
import { useEffect, useMemo, useRef, useState } from 'react'
import { css } from '../../../../../styled-system/css'
import { useViewerId } from '@/hooks/useViewerId'
import { css } from '../../../../../styled-system/css'
import { useMemoryPairs } from '../context/MemoryPairsContext'
import { getGridConfiguration } from '../utils/cardGeneration'
import { GameCard } from './GameCard'
@@ -88,11 +88,13 @@ function HoverAvatar({
playerInfo,
cardElement,
isPlayersTurn,
isCardFlipped,
}: {
playerId: string
playerInfo: { emoji: string; name: string; color?: string }
cardElement: HTMLElement | null
isPlayersTurn: boolean
isCardFlipped: boolean
}) {
const [position, setPosition] = useState<{ x: number; y: number } | null>(null)
const isFirstRender = useRef(true)
@@ -116,7 +118,8 @@ function HoverAvatar({
const springProps = useSpring({
x: position?.x ?? 0,
y: position?.y ?? 0,
opacity: position && isPlayersTurn && cardElement ? 1 : 0,
// Hide avatar if: no position, not player's turn, no card element, OR card is flipped
opacity: position && isPlayersTurn && cardElement && !isCardFlipped ? 1 : 0,
config: {
tension: 280,
friction: 60,
@@ -376,15 +379,13 @@ export function MemoryGrid() {
)}
{/* Animated Hover Avatars - Rendered as fixed positioned elements that smoothly transition */}
{/* Render one avatar per remote player - key by playerId to keep component alive */}
{/* Render one avatar per player - key by playerId to keep component alive */}
{state.playerHovers &&
Object.entries(state.playerHovers)
.filter(([playerId]) => {
// Don't show your own hover avatar (only show remote players)
// 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
// Only show avatar for the CURRENT player whose turn it is
// Don't show for other players (they're waiting for their turn)
return playerId === state.currentPlayer
})
.map(([playerId, cardId]) => {
const playerInfo = getPlayerHoverInfo(playerId)
@@ -392,6 +393,11 @@ export function MemoryGrid() {
const cardElement = cardId ? cardRefs.current.get(cardId) : null
// Check if it's this player's turn
const isPlayersTurn = state.currentPlayer === playerId
// Check if the card being hovered is flipped
const hoveredCard = cardId ? state.gameCards.find((c) => c.id === cardId) : null
const isCardFlipped = hoveredCard
? state.flippedCards.some((c) => c.id === hoveredCard.id) || hoveredCard.matched
: false
if (!playerInfo) return null
@@ -403,6 +409,7 @@ export function MemoryGrid() {
playerInfo={playerInfo}
cardElement={cardElement}
isPlayersTurn={isPlayersTurn}
isCardFlipped={isCardFlipped}
/>
)
})}

View File

@@ -1,5 +1,6 @@
'use client'
import { useViewerId } from '@/hooks/useViewerId'
import { css } from '../../../../../styled-system/css'
import { gamePlurals } from '../../../../utils/pluralization'
import { useMemoryPairs } from '../context/MemoryPairsContext'
@@ -10,6 +11,7 @@ interface PlayerStatusBarProps {
export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
const { state } = useMemoryPairs()
const { data: viewerId } = useViewerId()
// Get active players from game state (not GameModeContext)
// This ensures we only show players actually in this game
@@ -25,8 +27,8 @@ export function PlayerStatusBar({ className }: PlayerStatusBarProps) {
displayEmoji: player.emoji,
score: state.scores[player.id] || 0,
consecutiveMatches: state.consecutiveMatches?.[player.id] || 0,
// In local games all players are local, in room games check metadata
isLocalPlayer: state.gameMode === 'single' || state.gameMode === 'multiplayer',
// Check if this player belongs to the current viewer
isLocalPlayer: player.userId === viewerId,
}))
// Check if current player is local (your turn) or remote (waiting)

View File

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