# Composables Directory Vue 3 composables for shared logic. These handle WebSocket communication and game actions. ## Available Composables ### useWebSocket.ts **Purpose**: Manages Socket.io connection with authentication and auto-reconnection. **Key Exports**: ```typescript const { socket, // Computed isConnected, // Readonly> isConnecting, // Readonly> connectionError, // Readonly> connect, // () => void disconnect, // () => void } = useWebSocket() ``` **Event Flow** (Backend → Store): ``` socketInstance.on('game_state_update') → gameStore.setGameState() socketInstance.on('lineup_data') → gameStore.updateLineup() socketInstance.on('decision_required') → gameStore.setDecisionPrompt() socketInstance.on('play_completed') → gameStore.addPlayToHistory() socketInstance.on('dice_rolled') → gameStore.setPendingRoll() ``` **Singleton Pattern**: Socket instance is module-level, shared across all `useWebSocket()` calls. **Auto-Connect Pattern**: Uses `onMounted` hook (not `immediate: true` watcher) to connect when already authenticated. This is safer for SSR hydration: ```typescript // Watch for auth changes (not immediate) watch(() => authStore.isAuthenticated, (authenticated) => { if (authenticated && !isConnected.value) { connect() } else if (!authenticated && isConnected.value) { disconnect() } }) // Initial connection on client-side mount onMounted(() => { if (authStore.isAuthenticated && !isConnected.value) { connect() } }) ``` **Why not `immediate: true`?** Using `immediate: true` with `import.meta.client` guard can cause SSR hydration issues. The `onMounted` hook only runs on client-side, making it safer. ### useGameActions.ts **Purpose**: Wraps WebSocket emits with type safety and validation. **Key Exports**: ```typescript const { joinGame, // (gameId: string) => void requestGameState, // (gameId: string) => void setDefense, // (gameId: string, decision: DefensiveDecision) => void setOffense, // (gameId: string, decision: OffensiveDecision) => void rollDice, // (gameId: string) => void submitOutcome, // (gameId: string, outcome: PlayOutcome) => void // ... substitution methods } = useGameActions() ``` **Usage Pattern**: ```typescript // In component const { setDefense } = useGameActions() const handleSubmit = (decision: DefensiveDecision) => { setDefense(gameId, decision) } ``` ## Data Flow Architecture ``` User Action → useGameActions → socket.emit() → Backend ↓ Component ← gameStore ← useWebSocket.on() ← socket event ``` **Why this separation?** - `useWebSocket`: Low-level connection management, event routing - `useGameActions`: High-level game operations, business logic validation - `gameStore`: Centralized state, computed getters ## Common Issues | Issue | Cause | Solution | |-------|-------|----------| | "Not connected" error | Socket disconnected | Check `isConnected` before actions | | Events not firing | Listeners not set up | Ensure `useWebSocket()` called in component | | Stale data | Missed reconnection | Call `requestGameState()` after reconnect |