105 lines
2.7 KiB
JavaScript
105 lines
2.7 KiB
JavaScript
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
import { io } from 'socket.io-client';
|
|
import { useAuth } from './AuthContext';
|
|
|
|
const SocketContext = createContext();
|
|
|
|
export function useSocket() {
|
|
const context = useContext(SocketContext);
|
|
if (!context) {
|
|
throw new Error('useSocket must be used within SocketProvider');
|
|
}
|
|
return context;
|
|
}
|
|
|
|
export function SocketProvider({ children }) {
|
|
const [socket, setSocket] = useState(null);
|
|
const [connected, setConnected] = useState(false);
|
|
const { user, token } = useAuth();
|
|
|
|
useEffect(() => {
|
|
// Only connect if user is authenticated
|
|
if (!user || !token) {
|
|
console.log('⏳ Waiting for authentication...', { user: !!user, token: !!token });
|
|
if (socket) {
|
|
socket.disconnect();
|
|
setSocket(null);
|
|
setConnected(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
console.log('🔄 Initializing socket connection...');
|
|
|
|
// Determine backend URL (strip /api if present since socket.io is at root)
|
|
let backendUrl = import.meta.env.VITE_API_URL ||
|
|
(import.meta.env.DEV ? 'http://localhost:4000' : window.location.origin);
|
|
|
|
// Remove /api suffix if present
|
|
backendUrl = backendUrl.replace(/\/api$/, '');
|
|
|
|
console.log('🌐 Connecting to:', backendUrl);
|
|
|
|
// Create socket connection with JWT authentication
|
|
const newSocket = io(backendUrl, {
|
|
auth: {
|
|
token
|
|
},
|
|
reconnection: true,
|
|
reconnectionAttempts: 5,
|
|
reconnectionDelay: 1000
|
|
});
|
|
|
|
newSocket.on('connect', () => {
|
|
console.log('🔌 Socket connected');
|
|
setConnected(true);
|
|
});
|
|
|
|
newSocket.on('disconnect', () => {
|
|
console.log('❌ Socket disconnected');
|
|
setConnected(false);
|
|
});
|
|
|
|
newSocket.on('connect_error', (error) => {
|
|
console.error('❌ Socket connection error:', error.message);
|
|
console.error(' Error details:', error);
|
|
});
|
|
|
|
setSocket(newSocket);
|
|
|
|
// Cleanup on unmount
|
|
return () => {
|
|
newSocket.disconnect();
|
|
};
|
|
}, [user, token]);
|
|
|
|
const joinChallenge = (challengeId) => {
|
|
if (socket && connected) {
|
|
console.log(`📍 Joining challenge room: ${challengeId}`);
|
|
socket.emit('join:challenge', challengeId);
|
|
} else {
|
|
console.warn(`⚠️ Cannot join challenge ${challengeId} - socket:`, !!socket, 'connected:', connected);
|
|
}
|
|
};
|
|
|
|
const leaveChallenge = (challengeId) => {
|
|
if (socket && connected) {
|
|
console.log(`👋 Leaving challenge room: ${challengeId}`);
|
|
socket.emit('leave:challenge', challengeId);
|
|
}
|
|
};
|
|
|
|
const value = {
|
|
socket,
|
|
connected,
|
|
joinChallenge,
|
|
leaveChallenge
|
|
};
|
|
|
|
return (
|
|
<SocketContext.Provider value={value}>
|
|
{children}
|
|
</SocketContext.Provider>
|
|
);
|
|
}
|