@ngenux/ngage-whiteboarding
Version:
A collaborative whiteboard React component with real-time synchronization
178 lines (174 loc) • 6.58 kB
JavaScript
;
var socket_ioClient = require('socket.io-client');
let socket = null;
const joinedRooms = new Set();
const setupCallbacks = new Map();
let currentWebSocketUrl = undefined;
// Initialize socket connection
const initializeSocket = (webSocketUrl) => {
// Use provided webSocketUrl or fallback to environment variable for backward compatibility
const socketServerUrl = webSocketUrl || undefined?.VITE_WEBSOCKET_URL || "";
// If URL has changed or socket doesn't exist, create new connection
if (!socket || (webSocketUrl && currentWebSocketUrl !== webSocketUrl)) {
// Disconnect existing socket if URL changed
if (socket && currentWebSocketUrl !== webSocketUrl) {
socket.disconnect();
socket = null;
joinedRooms.clear();
setupCallbacks.clear();
}
currentWebSocketUrl = webSocketUrl;
console.log('[SOCKET] Using socket server URL:', socketServerUrl);
if (!socketServerUrl) {
console.error('[SOCKET] No socket server URL provided');
}
socket = socket_ioClient.io(socketServerUrl, {
transports: ['websocket'],
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
});
socket.on('connect', () => {
console.log('[SOCKET] Connected to server');
// Re-join all rooms after reconnection
joinedRooms.forEach(roomId => {
socket.emit('join-room', roomId);
console.log('[SOCKET] Re-joined room:', roomId);
});
});
socket.on('disconnect', () => {
console.log('[SOCKET] Disconnected from server');
});
socket.on('connect_error', (error) => {
console.error('[SOCKET] Connection error:', error);
});
// Set up the global receive-message listener once
socket.on('receive-message', (message) => {
const callback = setupCallbacks.get(message.roomId);
if (callback) {
console.log('[SOCKET] Received message from room:', message.roomId, {
compression: message.data.compressionType,
originalSize: message.data.originalSize,
compressedSize: message.data.compressedSize,
from: message.from
});
callback(message.data);
}
});
}
return socket;
};
const onSend = (roomId, data, webSocketUrl) => {
const socketInstance = initializeSocket(webSocketUrl);
// Check if socket is connected before sending
if (!socketInstance.connected) {
console.warn('[SOCKET] Socket not connected, cannot send message');
return;
}
const message = {
roomId,
data,
timestamp: Date.now(),
};
console.log('[SOCKET] Sending message to room:', roomId, {
compression: data.compressionType,
originalSize: data.originalSize,
compressedSize: data.compressedSize,
connected: socketInstance.connected,
socketId: socketInstance.id,
});
try {
socketInstance.emit('send-message', message);
console.log('[SOCKET] Message sent successfully');
}
catch (error) {
console.error('[SOCKET] Error sending message:', error);
}
};
const onReceive = (roomId, callback, webSocketUrl) => {
const socketInstance = initializeSocket(webSocketUrl);
// Store the callback for this room
setupCallbacks.set(roomId, callback);
// Only join the room if we haven't already
if (!joinedRooms.has(roomId)) {
socketInstance.emit('join-room', roomId);
joinedRooms.add(roomId);
console.log('[SOCKET] Joined room:', roomId);
}
else {
console.log('[SOCKET] Already in room:', roomId);
}
};
const leaveRoom = (roomId) => {
if (socket && joinedRooms.has(roomId)) {
socket.emit('leave-room', roomId);
joinedRooms.delete(roomId);
setupCallbacks.delete(roomId);
console.log('[SOCKET] Left room:', roomId);
}
};
const disconnectSocket = () => {
if (socket) {
socket.disconnect();
socket = null;
joinedRooms.clear();
setupCallbacks.clear();
console.log('[SOCKET] Socket disconnected and cleaned up');
}
};
// Get current socket connection status
const isSocketConnected = () => {
return socket?.connected ?? false;
};
// Get current socket instance
const getSocket = () => {
return socket;
};
// Subscribe to connection status changes
const onSocketStatusChange = (callback) => {
const socketInstance = socket || initializeSocket();
const handleConnect = () => callback(true);
const handleDisconnect = () => callback(false);
socketInstance.on('connect', handleConnect);
socketInstance.on('disconnect', handleDisconnect);
// Return unsubscribe function
return () => {
socketInstance.off('connect', handleConnect);
socketInstance.off('disconnect', handleDisconnect);
};
};
const waitForSocket = (webSocketUrl, timeoutMs = 5000) => {
return new Promise((resolve) => {
const socketInstance = initializeSocket(webSocketUrl);
// If already connected, resolve immediately
if (socketInstance.connected) {
console.log('[SOCKET] Socket already connected, resolving immediately');
resolve();
return;
}
console.log('[SOCKET] Waiting for socket connection (timeout: ' + timeoutMs + 'ms)');
// Listen for connect event
const handleConnect = () => {
console.log('[SOCKET] Socket connection established');
socketInstance.off('connect', handleConnect);
clearTimeout(timeoutHandle);
resolve();
};
socketInstance.on('connect', handleConnect);
// Safety net: resolve after timeout regardless
const timeoutHandle = setTimeout(() => {
console.log('[SOCKET] Connection timeout reached - resolving anyway (safety net)');
socketInstance.off('connect', handleConnect);
resolve();
}, timeoutMs);
});
};
exports.disconnectSocket = disconnectSocket;
exports.getSocket = getSocket;
exports.isSocketConnected = isSocketConnected;
exports.leaveRoom = leaveRoom;
exports.onReceive = onReceive;
exports.onSend = onSend;
exports.onSocketStatusChange = onSocketStatusChange;
exports.waitForSocket = waitForSocket;
//# sourceMappingURL=socket-utility.js.map