"use client"; import { createContext, useContext, useEffect, useState, ReactNode } from "react"; import { io, Socket } from "socket.io-client"; import { useAuth } from "./auth-context"; // Define the SocketContext type interface SocketContextType { socket: Socket | null; isConnected: boolean; joinProject: (projectId: string) => void; leaveProject: (projectId: string) => void; // Event listeners onProjectUpdated: (callback: (data: any) => void) => () => void; onCollaboratorAdded: (callback: (data: any) => void) => () => void; onGroupCreated: (callback: (data: any) => void) => () => void; onGroupUpdated: (callback: (data: any) => void) => () => void; onPersonAddedToGroup: (callback: (data: any) => void) => () => void; onPersonRemovedFromGroup: (callback: (data: any) => void) => () => void; onNotification: (callback: (data: any) => void) => () => void; } // Create the SocketContext const SocketContext = createContext(undefined); // Create a provider component export function SocketProvider({ children }: { children: ReactNode }) { const [socket, setSocket] = useState(null); const [isConnected, setIsConnected] = useState(false); const { user, isAuthenticated } = useAuth(); // Initialize socket connection when user is authenticated useEffect(() => { if (!isAuthenticated || !user) { return; } const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; // Create socket connection const socketInstance = io(API_URL, { withCredentials: true, query: { userId: user.id, }, }); // Set up event listeners socketInstance.on('connect', () => { console.log('Socket connected'); setIsConnected(true); }); socketInstance.on('disconnect', () => { console.log('Socket disconnected'); setIsConnected(false); }); socketInstance.on('connect_error', (error) => { console.error('Socket connection error:', error); setIsConnected(false); }); // Save socket instance setSocket(socketInstance); // Clean up on unmount return () => { socketInstance.disconnect(); setSocket(null); setIsConnected(false); }; }, [isAuthenticated, user]); // Join a project room const joinProject = (projectId: string) => { if (socket && isConnected) { socket.emit('project:join', projectId); } }; // Leave a project room const leaveProject = (projectId: string) => { if (socket && isConnected) { socket.emit('project:leave', projectId); } }; // Event listeners with cleanup const onProjectUpdated = (callback: (data: any) => void) => { if (socket) { socket.on('project:updated', callback); } return () => { if (socket) { socket.off('project:updated', callback); } }; }; const onCollaboratorAdded = (callback: (data: any) => void) => { if (socket) { socket.on('project:collaboratorAdded', callback); } return () => { if (socket) { socket.off('project:collaboratorAdded', callback); } }; }; const onGroupCreated = (callback: (data: any) => void) => { if (socket) { socket.on('group:created', callback); } return () => { if (socket) { socket.off('group:created', callback); } }; }; const onGroupUpdated = (callback: (data: any) => void) => { if (socket) { socket.on('group:updated', callback); } return () => { if (socket) { socket.off('group:updated', callback); } }; }; const onPersonAddedToGroup = (callback: (data: any) => void) => { if (socket) { socket.on('group:personAdded', callback); } return () => { if (socket) { socket.off('group:personAdded', callback); } }; }; const onPersonRemovedFromGroup = (callback: (data: any) => void) => { if (socket) { socket.on('group:personRemoved', callback); } return () => { if (socket) { socket.off('group:personRemoved', callback); } }; }; const onNotification = (callback: (data: any) => void) => { if (socket) { socket.on('notification:new', callback); } return () => { if (socket) { socket.off('notification:new', callback); } }; }; // Create the context value const value = { socket, isConnected, joinProject, leaveProject, onProjectUpdated, onCollaboratorAdded, onGroupCreated, onGroupUpdated, onPersonAddedToGroup, onPersonRemovedFromGroup, onNotification, }; return {children}; } // Create a hook to use the SocketContext export function useSocket() { const context = useContext(SocketContext); if (context === undefined) { throw new Error("useSocket must be used within a SocketProvider"); } return context; }