diff --git a/frontend-sba/composables/useWebSocket.ts b/frontend-sba/composables/useWebSocket.ts index 43bf1c9..8976553 100644 --- a/frontend-sba/composables/useWebSocket.ts +++ b/frontend-sba/composables/useWebSocket.ts @@ -139,6 +139,38 @@ export function useWebSocket() { reconnectionAttempts = 0 } + /** + * Force reconnect - clears the singleton and creates a fresh connection. + * Use this when the connection is in a bad state and normal reconnection isn't working. + */ + function forceReconnect() { + console.log('[WebSocket] Force reconnecting - clearing singleton') + + // Clear reconnection timer + if (reconnectionTimeout) { + clearTimeout(reconnectionTimeout) + reconnectionTimeout = null + } + + // Fully disconnect and destroy the socket instance + if (socketInstance) { + socketInstance.removeAllListeners() + socketInstance.disconnect() + socketInstance = null + } + + // Reset all state + isConnected.value = false + isConnecting.value = false + connectionError.value = null + reconnectionAttempts = 0 + + // Reconnect after a short delay + setTimeout(() => { + connect() + }, 100) + } + /** * Reconnect with exponential backoff */ @@ -473,8 +505,10 @@ export function useWebSocket() { // Watch for auth changes (not immediate - onMounted handles initial state) watch( () => authStore.isAuthenticated, - (authenticated) => { + (authenticated, oldValue) => { + console.log('[WebSocket] Auth changed:', oldValue, '->', authenticated) if (authenticated && !isConnected.value && !isConnecting.value) { + console.log('[WebSocket] Auth became true, connecting...') connect() startHeartbeat() } else if (!authenticated && isConnected.value) { @@ -489,9 +523,18 @@ export function useWebSocket() { // ============================================================================ // Initial connection on client-side mount (handles SSR hydration case) - onMounted(() => { + onMounted(async () => { + console.log('[WebSocket] onMounted - isAuthenticated:', authStore.isAuthenticated) + + // If not authenticated, try to check auth first (handles SSR hydration case) + if (!authStore.isAuthenticated) { + console.log('[WebSocket] Not authenticated on mount, checking auth...') + await authStore.checkAuth() + console.log('[WebSocket] After checkAuth - isAuthenticated:', authStore.isAuthenticated) + } + if (authStore.isAuthenticated && !isConnected.value && !isConnecting.value) { - console.log('[WebSocket] Auto-connecting on mount (already authenticated)') + console.log('[WebSocket] Auto-connecting on mount (authenticated)') connect() startHeartbeat() } @@ -519,5 +562,6 @@ export function useWebSocket() { // Actions connect, disconnect, + forceReconnect, } } diff --git a/frontend-sba/pages/games/[id].vue b/frontend-sba/pages/games/[id].vue index 2663749..1f7f01c 100755 --- a/frontend-sba/pages/games/[id].vue +++ b/frontend-sba/pages/games/[id].vue @@ -19,18 +19,32 @@ v-if="!isConnected" class="mb-4 bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-lg" > -
-
- - - - -
-
-

- {{ connectionStatus === 'connecting' ? 'Connecting to game server...' : 'Disconnected from server. Attempting to reconnect...' }} -

+
+
+
+ + + + +
+
+

+ {{ connectionStatus === 'connecting' ? 'Connecting to game server...' : 'Disconnected from server. Attempting to reconnect...' }} +

+ +

+ Auth: {{ authStore.isAuthenticated ? 'Yes' : 'No' }} | + Connecting: {{ isConnecting ? 'Yes' : 'No' }} | + Error: {{ connectionError || 'None' }} +

+
+
@@ -169,6 +183,21 @@

Loading game...

+ +

+ Auth: {{ authStore.isAuthenticated ? 'Yes' : 'No' }} | + Connected: {{ isConnected ? 'Yes' : 'No' }} | + Connecting: {{ isConnecting ? 'Yes' : 'No' }} +

+

+ Error: {{ connectionError || 'None' }} +

+
@@ -298,7 +327,7 @@ const uiStore = useUiStore() const gameId = computed(() => route.params.id as string) // WebSocket connection -const { socket, isConnected, connectionError, connect } = useWebSocket() +const { socket, isConnected, isConnecting, connectionError, connect, forceReconnect } = useWebSocket() // Pass the raw string value from route params, not computed value // useGameActions will create its own computed internally if needed @@ -571,6 +600,27 @@ const handleUndoLastPlay = () => { undoLastPlay(1) } +// Retry connection with auth check +const retryWithAuth = async () => { + console.log('[Game Page] Retry with auth check') + isLoading.value = true + connectionStatus.value = 'connecting' + + // First check auth + const isAuthed = await authStore.checkAuth() + console.log('[Game Page] Auth result:', isAuthed, 'isAuthenticated:', authStore.isAuthenticated) + + if (!isAuthed) { + console.error('[Game Page] Auth failed') + isLoading.value = false + connectionStatus.value = 'disconnected' + return + } + + // Force reconnect + forceReconnect() +} + // Measure ScoreBoard height dynamically const updateScoreBoardHeight = () => { if (scoreBoardRef.value) {