diff --git a/app/hooks/useDraftSocket.ts b/app/hooks/useDraftSocket.ts new file mode 100644 index 0000000..f0772c9 --- /dev/null +++ b/app/hooks/useDraftSocket.ts @@ -0,0 +1,61 @@ +import { useEffect, useRef, useState } from "react"; +import { io, Socket } from "socket.io-client"; + +interface UseDraftSocketReturn { + socket: Socket | null; + isConnected: boolean; + on: (event: string, callback: (...args: any[]) => void) => void; + off: (event: string, callback?: (...args: any[]) => void) => void; +} + +export function useDraftSocket(seasonId: string): UseDraftSocketReturn { + const socketRef = useRef(null); + const [isConnected, setIsConnected] = useState(false); + + useEffect(() => { + // Connect to Socket.IO server + const socket = io({ + path: "/socket.io", + transports: ["websocket", "polling"], + }); + + socketRef.current = socket; + + socket.on("connect", () => { + console.log("Connected to Socket.IO:", socket.id); + setIsConnected(true); + // Join the draft room + socket.emit("join-draft", seasonId); + }); + + socket.on("disconnect", () => { + console.log("Disconnected from Socket.IO"); + setIsConnected(false); + }); + + socket.on("connect_error", (error) => { + console.error("Socket.IO connection error:", error); + }); + + // Cleanup on unmount + return () => { + if (socket) { + console.log("Leaving draft room:", seasonId); + socket.emit("leave-draft", seasonId); + socket.disconnect(); + } + }; + }, [seasonId]); + + // Helper to subscribe to events + const on = (event: string, callback: (...args: any[]) => void) => { + socketRef.current?.on(event, callback); + }; + + // Helper to unsubscribe from events + const off = (event: string, callback?: (...args: any[]) => void) => { + socketRef.current?.off(event, callback); + }; + + return { socket: socketRef.current, isConnected, on, off }; +} diff --git a/app/routes.ts b/app/routes.ts index 1e08d0c..c71d701 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -10,7 +10,8 @@ export default [ route("api/webhooks/clerk", "routes/api/webhooks/clerk.ts"), route("user-profile", "routes/user-profile.tsx"), route("how-to-play", "routes/how-to-play.tsx"), - + route("test-socket", "routes/test-socket.tsx"), + // Admin routes route("admin", "routes/admin.tsx", [ index("routes/admin._index.tsx"), @@ -20,12 +21,18 @@ export default [ route("sports-seasons", "routes/admin.sports-seasons.tsx"), route("sports-seasons/new", "routes/admin.sports-seasons.new.tsx"), route("sports-seasons/:id", "routes/admin.sports-seasons.$id.tsx"), - route("sports-seasons/:id/participants", "routes/admin.sports-seasons.$id.participants.tsx"), + route( + "sports-seasons/:id/participants", + "routes/admin.sports-seasons.$id.participants.tsx" + ), route("participants", "routes/admin.participants.tsx"), route("templates", "routes/admin.templates.tsx"), route("templates/new", "routes/admin.templates.new.tsx"), route("templates/:id", "routes/admin.templates.$id.tsx"), route("data-sync", "routes/admin.data-sync.tsx"), ]), - route("api/admin/export-sports-data", "routes/api.admin.export-sports-data.ts"), + route( + "api/admin/export-sports-data", + "routes/api.admin.export-sports-data.ts" + ), ] satisfies RouteConfig; diff --git a/app/routes/test-socket.tsx b/app/routes/test-socket.tsx new file mode 100644 index 0000000..446ac21 --- /dev/null +++ b/app/routes/test-socket.tsx @@ -0,0 +1,90 @@ +import { useState, useEffect } from "react"; +import { useDraftSocket } from "~/hooks/useDraftSocket"; + +export default function TestSocket() { + const [messages, setMessages] = useState([]); + const { socket, isConnected, on, off } = useDraftSocket("test-season-123"); + + useEffect(() => { + // Listen for test messages + const handleTestMessage = (data: any) => { + console.log("Received test message:", data); + setMessages((prev) => [...prev, JSON.stringify(data)]); + }; + + on("test-message", handleTestMessage); + + return () => { + off("test-message", handleTestMessage); + }; + }, [on, off]); + + const sendTestMessage = () => { + if (socket) { + socket.emit("test-event", { + message: "Hello from client!", + timestamp: new Date().toISOString(), + }); + setMessages((prev) => [...prev, "Sent: Hello from client!"]); + } + }; + + return ( +
+

Socket.IO Test Page

+ +
+
+
+ + Status: {isConnected ? "Connected" : "Disconnected"} + +
+ {socket && ( +

+ Socket ID: {socket.id} +

+ )} +
+ + + +
+

Messages:

+
+ {messages.length === 0 ? ( +

No messages yet...

+ ) : ( +
    + {messages.map((msg, idx) => ( +
  • + {msg} +
  • + ))} +
+ )} +
+
+ +
+

Instructions:

+
    +
  1. Check that the status shows "Connected"
  2. +
  3. Open browser console to see connection logs
  4. +
  5. Check server logs for "Client connected" message
  6. +
  7. Click "Send Test Message" to test two-way communication
  8. +
+
+
+ ); +} diff --git a/plans/phase-4-socket-test-instructions.md b/plans/phase-4-socket-test-instructions.md new file mode 100644 index 0000000..ab1475d --- /dev/null +++ b/plans/phase-4-socket-test-instructions.md @@ -0,0 +1,118 @@ +# Phase 4: Socket.IO Two-Way Communication Test + +## What's Been Implemented: + +### Server-Side (`server/socket.js`): +- ✅ Listens for `test-event` from clients +- ✅ Logs received message to console with 📨 emoji +- ✅ Echoes back with `test-message` event containing: + - Original client message + - Server response: "Hello from server!" + - Server timestamp + - Socket ID +- ✅ Logs sent response with ✅ emoji + +### Client-Side (`app/routes/test-socket.tsx`): +- ✅ Connects to Socket.IO server +- ✅ Shows connection status (green dot = connected) +- ✅ Displays Socket ID +- ✅ "Send Test Message" button emits `test-event` +- ✅ Listens for `test-message` responses +- ✅ Displays all messages in a log + +## How to Test: + +### Step 1: Navigate to Test Page +``` +http://localhost:3000/test-socket +``` + +### Step 2: Verify Connection +You should see: +- ✅ Green dot with "Status: Connected" +- ✅ Socket ID displayed (e.g., "Socket ID: abc123") + +**Server Console Should Show:** +``` +Client connected: abc123 +Socket abc123 joined draft-test-season-123 +``` + +### Step 3: Test Two-Way Communication + +**Click the "Send Test Message" button** + +**What Happens:** + +1. **Client → Server:** + - Client emits `test-event` with message and timestamp + - Message appears in the Messages log: "Sent: Hello from client!" + +2. **Server Logs:** + ``` + 📨 Received test-event from client: abc123 { message: 'Hello from client!', timestamp: '...' } + ✅ Sent test-message response to client: abc123 + ``` + +3. **Server → Client:** + - Server emits `test-message` back to client + - Client receives and displays the response in Messages log + +4. **Browser Should Show:** + ``` + Sent: Hello from client! + {"originalMessage":{"message":"Hello from client!","timestamp":"..."},"serverResponse":"Hello from server!","serverTimestamp":"...","socketId":"abc123"} + ``` + +### Step 4: Verify Multiple Messages +- Click "Send Test Message" multiple times +- Each click should: + - Add a new message to the log + - Show server response in console + - Display echo response in browser + +## Success Criteria: + +✅ Connection established (green dot) +✅ Socket ID displayed +✅ Server logs show "Client connected" +✅ Server logs show "joined draft-test-season-123" +✅ Clicking button logs 📨 on server +✅ Server logs ✅ after sending response +✅ Browser displays both sent and received messages +✅ Server response includes original message + server timestamp + +## What This Proves: + +1. ✅ **Client can connect to server** - WebSocket connection established +2. ✅ **Client → Server** - Client can send events to server +3. ✅ **Server receives events** - Server processes client messages +4. ✅ **Server → Client** - Server can send events to specific clients +5. ✅ **Client receives events** - Client processes server messages +6. ✅ **Full round-trip** - Complete two-way communication working + +## Next Steps: + +Once this test passes, you're ready to: +1. Implement draft room UI +2. Add real-time pick broadcasting +3. Implement timer updates +4. Add queue management +5. Deploy to production with confidence! + +## Troubleshooting: + +**If connection fails:** +- Check server console for "Socket.IO initialized" +- Check browser console for connection errors +- Verify port 3000 is not blocked + +**If messages don't appear:** +- Check browser console for errors +- Verify server logs show 📨 emoji +- Check that `test-event` and `test-message` event names match + +**If server doesn't respond:** +- Restart dev server +- Check server/socket.js has test-event handler +- Verify no JavaScript errors in server console diff --git a/server/socket.js b/server/socket.js index e6c4db1..6511972 100644 --- a/server/socket.js +++ b/server/socket.js @@ -46,6 +46,21 @@ export function initializeSocketIO(httpServer) { console.log(`Socket ${socket.id} left draft-${seasonId}`); }); + // Test event handler - echo back with server timestamp + socket.on("test-event", (data) => { + console.log("📨 Received test-event from client:", socket.id, data); + + // Echo back to the client with server response + socket.emit("test-message", { + originalMessage: data, + serverResponse: "Hello from server!", + serverTimestamp: new Date().toISOString(), + socketId: socket.id, + }); + + console.log("✅ Sent test-message response to client:", socket.id); + }); + socket.on("disconnect", () => { console.log("Client disconnected:", socket.id); });