Compare commits

...

4 Commits

Author SHA1 Message Date
semantic-release-bot
9fa5652173 chore(release): 2.4.5 [skip ci]
## [2.4.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.4.4...v2.4.5) (2025-10-08)

### Bug Fixes

* send all members (not just online) in socket broadcasts ([3fa6cce](3fa6cce17a))
2025-10-08 14:03:35 +00:00
Thomas Hallock
3fa6cce17a fix: send all members (not just online) in socket broadcasts
The root issue was that socket broadcasts were sending only online
members while the initial page load showed all members, causing
inconsistency.

Changes:
- Join/leave API routes now send `members` (all) instead of `onlineMembers`
- Socket server join/leave handlers now send `members` instead of `onlineMembers`
- Client event handlers updated to expect `data.members` instead of `data.onlineMembers`
- Removed unused `getOnlineRoomMembers` import from socket-server.ts

Now when a user joins, other users immediately see them in the members
list without needing to reload the page. Both members and players are
updated together in the same broadcast.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 09:02:44 -05:00
semantic-release-bot
8f3dd9ec92 chore(release): 2.4.4 [skip ci]
## [2.4.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.4.3...v2.4.4) (2025-10-08)

### Bug Fixes

* correctly access getSocketIO from dynamic import ([30abf33](30abf33ee8))
2025-10-08 13:57:35 +00:00
Thomas Hallock
30abf33ee8 fix: correctly access getSocketIO from dynamic import
The dynamic import returns a module namespace object, so we need to
access socketServerModule.getSocketIO() rather than treating the
module itself as the function.

Simplified the wrapper to directly cache and use the module, checking
that getSocketIO exists before calling it.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 08:56:47 -05:00
7 changed files with 55 additions and 47 deletions

View File

@@ -1,3 +1,17 @@
## [2.4.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.4.4...v2.4.5) (2025-10-08)
### Bug Fixes
* send all members (not just online) in socket broadcasts ([3fa6cce](https://github.com/antialias/soroban-abacus-flashcards/commit/3fa6cce17a7acd940cf5a9e6433bf6c4b497540c))
## [2.4.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.4.3...v2.4.4) (2025-10-08)
### Bug Fixes
* correctly access getSocketIO from dynamic import ([30abf33](https://github.com/antialias/soroban-abacus-flashcards/commit/30abf33ee86b36f2a98014e5b017fa8e466a2107))
## [2.4.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.4.2...v2.4.3) (2025-10-08)

View File

@@ -9,12 +9,7 @@ import {
updateSessionActivity,
} from './src/lib/arcade/session-manager'
import { createRoom, getRoomById } from './src/lib/arcade/room-manager'
import {
getOnlineRoomMembers,
getRoomMembers,
getUserRooms,
setMemberOnline,
} from './src/lib/arcade/room-membership'
import { getRoomMembers, getUserRooms, setMemberOnline } from './src/lib/arcade/room-membership'
import { getRoomActivePlayers } from './src/lib/arcade/player-manager'
import type { GameMove, GameName } from './src/lib/arcade/validation'
import { matchingGameValidator } from './src/lib/arcade/validation/MatchingGameValidator'
@@ -232,7 +227,6 @@ export function initializeSocketServer(httpServer: HTTPServer) {
// Get room data
const members = await getRoomMembers(roomId)
const onlineMembers = await getOnlineRoomMembers(roomId)
const memberPlayers = await getRoomActivePlayers(roomId)
// Convert memberPlayers Map to object for JSON serialization
@@ -245,7 +239,6 @@ export function initializeSocketServer(httpServer: HTTPServer) {
socket.emit('room-joined', {
roomId,
members,
onlineMembers,
memberPlayers: memberPlayersObj,
})
@@ -253,7 +246,7 @@ export function initializeSocketServer(httpServer: HTTPServer) {
socket.to(`room:${roomId}`).emit('member-joined', {
roomId,
userId,
onlineMembers,
members,
memberPlayers: memberPlayersObj,
})
@@ -275,8 +268,8 @@ export function initializeSocketServer(httpServer: HTTPServer) {
// Mark member as offline
await setMemberOnline(roomId, userId, false)
// Get updated online members
const onlineMembers = await getOnlineRoomMembers(roomId)
// Get updated members
const members = await getRoomMembers(roomId)
const memberPlayers = await getRoomActivePlayers(roomId)
// Convert memberPlayers Map to object
@@ -289,7 +282,7 @@ export function initializeSocketServer(httpServer: HTTPServer) {
io.to(`room:${roomId}`).emit('member-left', {
roomId,
userId,
onlineMembers,
members,
memberPlayers: memberPlayersObj,
})

View File

@@ -1,6 +1,6 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getRoomById, touchRoom } from '@/lib/arcade/room-manager'
import { addRoomMember, getOnlineRoomMembers } from '@/lib/arcade/room-membership'
import { addRoomMember, getRoomMembers } from '@/lib/arcade/room-membership'
import { getActivePlayers, getRoomActivePlayers } from '@/lib/arcade/player-manager'
import { getViewerId } from '@/lib/viewer'
import { getSocketIO } from '@/lib/socket-io'
@@ -61,7 +61,7 @@ export async function POST(req: NextRequest, context: RouteContext) {
const io = await getSocketIO()
if (io) {
try {
const onlineMembers = await getOnlineRoomMembers(roomId)
const members = await getRoomMembers(roomId)
const memberPlayers = await getRoomActivePlayers(roomId)
// Convert memberPlayers Map to object for JSON serialization
@@ -74,7 +74,7 @@ export async function POST(req: NextRequest, context: RouteContext) {
io.to(`room:${roomId}`).emit('member-joined', {
roomId,
userId: viewerId,
onlineMembers,
members,
memberPlayers: memberPlayersObj,
})

View File

@@ -1,6 +1,6 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getRoomById } from '@/lib/arcade/room-manager'
import { getOnlineRoomMembers, isMember, removeMember } from '@/lib/arcade/room-membership'
import { getRoomMembers, isMember, removeMember } from '@/lib/arcade/room-membership'
import { getRoomActivePlayers } from '@/lib/arcade/player-manager'
import { getViewerId } from '@/lib/viewer'
import { getSocketIO } from '@/lib/socket-io'
@@ -37,7 +37,7 @@ export async function POST(_req: NextRequest, context: RouteContext) {
const io = await getSocketIO()
if (io) {
try {
const onlineMembers = await getOnlineRoomMembers(roomId)
const members = await getRoomMembers(roomId)
const memberPlayers = await getRoomActivePlayers(roomId)
// Convert memberPlayers Map to object for JSON serialization
@@ -50,7 +50,7 @@ export async function POST(_req: NextRequest, context: RouteContext) {
io.to(`room:${roomId}`).emit('member-left', {
roomId,
userId: viewerId,
onlineMembers,
members,
memberPlayers: memberPlayersObj,
})

View File

@@ -83,8 +83,8 @@ export default function RoomDetailPage() {
sock.on('member-joined', (data) => {
console.log('Member joined:', data)
if (data.onlineMembers) {
setMembers(data.onlineMembers)
if (data.members) {
setMembers(data.members)
}
if (data.memberPlayers) {
setMemberPlayers(data.memberPlayers)
@@ -93,8 +93,8 @@ export default function RoomDetailPage() {
sock.on('member-left', (data) => {
console.log('Member left:', data)
if (data.onlineMembers) {
setMembers(data.onlineMembers)
if (data.members) {
setMembers(data.members)
}
if (data.memberPlayers) {
setMemberPlayers(data.memberPlayers)

View File

@@ -6,34 +6,35 @@
import type { Server as SocketIOServerType } from 'socket.io'
// Import the socket server module (this is safe because it's only used in Node.js context)
let socketServer: { getSocketIO: () => SocketIOServerType | null } | null = null
// Lazy-load the socket server module (only works on server-side)
async function loadSocketServer() {
if (typeof window !== 'undefined') {
// Client-side: return null
return null
}
if (!socketServer) {
try {
// Dynamic import to avoid bundling issues
socketServer = await import('../../socket-server')
} catch (error) {
console.error('[Socket IO] Failed to load socket server:', error)
return null
}
}
return socketServer
}
// Cache for the socket server module
let socketServerModule: any = null
/**
* Get the socket.io server instance
* Returns null if not initialized or if called on client-side
*/
export async function getSocketIO(): Promise<SocketIOServerType | null> {
const server = await loadSocketServer()
return server ? server.getSocketIO() : null
// Client-side: return null
if (typeof window !== 'undefined') {
return null
}
// Lazy-load the socket server module on first call
if (!socketServerModule) {
try {
// Dynamic import to avoid bundling issues
socketServerModule = await import('../../socket-server')
} catch (error) {
console.error('[Socket IO] Failed to load socket server:', error)
return null
}
}
// Call the exported getSocketIO function from the module
if (socketServerModule && typeof socketServerModule.getSocketIO === 'function') {
return socketServerModule.getSocketIO()
}
console.warn('[Socket IO] getSocketIO function not found in socket-server module')
return null
}

View File

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