fix: remove duplicate ModerationNotifications causing double toasts

**Root Cause:**
- ModerationNotifications was rendered in BOTH /arcade/room/page.tsx AND PageWithNav
- Both had separate useRoomData hooks, creating two socket listeners
- When join-request-submitted event fired, BOTH instances showed a toast
- Clicking "Approve" only closed one toast, leaving the other visible

**Fix:**
- Removed ModerationNotifications from room page entirely
- PageWithNav (inside MemoryPairsGame) already handles all moderation events
- Now only ONE instance listens and renders, so approvals properly dismiss

**Files Changed:**
- Removed ModerationNotifications import from room page
- Removed all 4 instances of <ModerationNotifications /> from room page
- Removed moderationEvent and clearModerationEvent from useRoomData destructuring
- Added comment explaining PageWithNav handles notifications

🤖 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-14 08:17:58 -05:00
parent cb2fec1da5
commit c6886a0e59

View File

@@ -1,7 +1,6 @@
'use client'
import { useRoomData } from '@/hooks/useRoomData'
import { ModerationNotifications } from '@/components/nav/ModerationNotifications'
import { MemoryPairsGame } from '../matching/components/MemoryPairsGame'
import { RoomMemoryPairsProvider } from '../matching/context/RoomMemoryPairsProvider'
@@ -11,60 +10,57 @@ import { RoomMemoryPairsProvider } from '../matching/context/RoomMemoryPairsProv
*
* Note: We don't redirect to /arcade if no room exists to avoid navigation loops.
* Instead, we show a friendly message with a link back to the Champion Arena.
*
* Note: ModerationNotifications is handled by PageWithNav inside each game component,
* so we don't need to render it here.
*/
export default function RoomPage() {
const { roomData, isLoading, moderationEvent, clearModerationEvent } = useRoomData()
const { roomData, isLoading } = useRoomData()
// Show loading state
if (isLoading) {
return (
<>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
}}
>
Loading room...
</div>
<ModerationNotifications moderationEvent={moderationEvent} onClose={clearModerationEvent} />
</>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
}}
>
Loading room...
</div>
)
}
// Show error if no room (instead of redirecting)
if (!roomData) {
return (
<>
<div
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
gap: '1rem',
}}
>
<div>No active room found</div>
<a
href="/arcade"
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
gap: '1rem',
color: '#3b82f6',
textDecoration: 'underline',
}}
>
<div>No active room found</div>
<a
href="/arcade"
style={{
color: '#3b82f6',
textDecoration: 'underline',
}}
>
Go to Champion Arena
</a>
</div>
<ModerationNotifications moderationEvent={moderationEvent} onClose={clearModerationEvent} />
</>
Go to Champion Arena
</a>
</div>
)
}
@@ -72,38 +68,26 @@ export default function RoomPage() {
switch (roomData.gameName) {
case 'matching':
return (
<>
<RoomMemoryPairsProvider>
<MemoryPairsGame />
</RoomMemoryPairsProvider>
<ModerationNotifications
moderationEvent={moderationEvent}
onClose={clearModerationEvent}
/>
</>
<RoomMemoryPairsProvider>
<MemoryPairsGame />
</RoomMemoryPairsProvider>
)
// TODO: Add other games (complement-race, memory-quiz, etc.)
default:
return (
<>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
}}
>
Game "{roomData.gameName}" not yet supported
</div>
<ModerationNotifications
moderationEvent={moderationEvent}
onClose={clearModerationEvent}
/>
</>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
fontSize: '18px',
color: '#666',
}}
>
Game "{roomData.gameName}" not yet supported
</div>
)
}
}