diff --git a/apps/web/src/app/join/[code]/page.tsx b/apps/web/src/app/join/[code]/page.tsx index 9dff71b2..70980543 100644 --- a/apps/web/src/app/join/[code]/page.tsx +++ b/apps/web/src/app/join/[code]/page.tsx @@ -2,6 +2,7 @@ import { useRouter } from 'next/navigation' import { useCallback, useEffect, useState } from 'react' +import { io } from 'socket.io-client' import { useGetRoomByCode, useJoinRoom, useRoomData } from '@/hooks/useRoomData' import { getRoomDisplayWithEmoji } from '@/utils/room-display' @@ -351,6 +352,61 @@ export default function JoinRoomPage({ params }: { params: { code: string } }) { } } + // Socket listener and polling for approval notifications + useEffect(() => { + if (!approvalRequested || !targetRoomData) return + + console.log('[Join Page] Setting up approval listeners for room:', targetRoomData.id) + + // Socket listener for real-time approval notification + const socket = io({ path: '/api/socket' }) + + socket.on('connect', () => { + console.log('[Join Page] Socket connected') + }) + + socket.on('join-request-approved', (data: { roomId: string; requestId: string }) => { + console.log('[Join Page] Request approved via socket!', data) + if (data.roomId === targetRoomData.id) { + console.log('[Join Page] Joining room automatically...') + handleJoin(targetRoomData.id) + } + }) + + socket.on('connect_error', (error) => { + console.error('[Join Page] Socket connection error:', error) + }) + + // Polling fallback - check every 5 seconds + const pollInterval = setInterval(async () => { + try { + console.log('[Join Page] Polling for approval status...') + const res = await fetch(`/api/arcade/rooms/${targetRoomData.id}/join-requests`) + if (res.ok) { + const data = await res.json() + // Check if any request for this user was approved + const approvedRequest = data.requests?.find( + (r: { status: string }) => r.status === 'approved' + ) + if (approvedRequest) { + console.log('[Join Page] Request approved via polling! Joining room...') + clearInterval(pollInterval) + socket.disconnect() + handleJoin(targetRoomData.id) + } + } + } catch (err) { + console.error('[Join Page] Failed to poll join requests:', err) + } + }, 5000) + + return () => { + console.log('[Join Page] Cleaning up approval listeners') + socket.disconnect() + clearInterval(pollInterval) + } + }, [approvalRequested, targetRoomData, handleJoin]) + // Only show error page for non-password and non-approval errors if (error && !showPasswordPrompt && !showApprovalPrompt) { return ( diff --git a/apps/web/src/components/nav/JoinRoomModal.tsx b/apps/web/src/components/nav/JoinRoomModal.tsx index 3c91764e..327a4a40 100644 --- a/apps/web/src/components/nav/JoinRoomModal.tsx +++ b/apps/web/src/components/nav/JoinRoomModal.tsx @@ -1,4 +1,5 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' +import { io } from 'socket.io-client' import { Modal } from '@/components/common/Modal' import type { schema } from '@/db' import { useRoomData } from '@/hooks/useRoomData' @@ -142,6 +143,75 @@ export function JoinRoomModal({ isOpen, onClose, onSuccess }: JoinRoomModalProps } } + // Socket listener and polling for approval notifications + useEffect(() => { + if (!approvalRequested || !roomInfo) return + + console.log('[JoinRoomModal] Setting up approval listeners for room:', roomInfo.id) + + // Socket listener for real-time approval notification + const socket = io({ path: '/api/socket' }) + + socket.on('connect', () => { + console.log('[JoinRoomModal] Socket connected') + }) + + socket.on('join-request-approved', async (data: { roomId: string; requestId: string }) => { + console.log('[JoinRoomModal] Request approved via socket!', data) + if (data.roomId === roomInfo.id) { + console.log('[JoinRoomModal] Joining room automatically...') + try { + await joinRoom(roomInfo.id) + handleClose() + onSuccess?.() + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to join room') + setIsLoading(false) + } + } + }) + + socket.on('connect_error', (error) => { + console.error('[JoinRoomModal] Socket connection error:', error) + }) + + // Polling fallback - check every 5 seconds + const pollInterval = setInterval(async () => { + try { + console.log('[JoinRoomModal] Polling for approval status...') + const res = await fetch(`/api/arcade/rooms/${roomInfo.id}/join-requests`) + if (res.ok) { + const data = await res.json() + // Check if any request for this user was approved + const approvedRequest = data.requests?.find( + (r: { status: string }) => r.status === 'approved' + ) + if (approvedRequest) { + console.log('[JoinRoomModal] Request approved via polling! Joining room...') + clearInterval(pollInterval) + socket.disconnect() + try { + await joinRoom(roomInfo.id) + handleClose() + onSuccess?.() + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to join room') + setIsLoading(false) + } + } + } + } catch (err) { + console.error('[JoinRoomModal] Failed to poll join requests:', err) + } + }, 5000) + + return () => { + console.log('[JoinRoomModal] Cleaning up approval listeners') + socket.disconnect() + clearInterval(pollInterval) + } + }, [approvalRequested, roomInfo, joinRoom, handleClose, onSuccess]) + return (