Resolved WebSocket connection issues and games list loading on iPad: - cookies.py: Added is_secure_context() to set Secure flag when accessed via HTTPS even in development mode (Safari requires this) - useWebSocket.ts: Changed auto-connect from immediate watcher to onMounted hook for safer SSR hydration - games/index.vue: Replaced onMounted + fetchGames() with useAsyncData for SSR data fetching with proper cookie forwarding - Updated COOKIE_AUTH_IMPLEMENTATION.md with new issues and solutions - Updated composables/CLAUDE.md with auto-connect pattern documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
3.2 KiB
3.2 KiB
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:
const {
socket, // Computed<TypedSocket | null>
isConnected, // Readonly<Ref<boolean>>
isConnecting, // Readonly<Ref<boolean>>
connectionError, // Readonly<Ref<string | null>>
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:
// 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:
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:
// 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 routinguseGameActions: High-level game operations, business logic validationgameStore: 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 |