in src/components/Widget/index.js [441:586]
registerSocketHandlers(sendInitPayload = true) {
const {
storage,
socket,
dispatch,
connectOn,
tooltipPayload,
tooltipDelay,
customData
} = this.props;
// Skip if handlers already registered to prevent duplicates
if (this.socketHandlersRegistered) {
logger.info('⏭️ Socket handlers already registered, skipping...');
return;
}
logger.info('📝 Registering socket event handlers...');
// Register bot_uttered handler
socket.on('bot_uttered', (botUttered) => {
this.handleBotUtterance(botUttered);
});
// Register connect handler - CRITICAL for session_request
const sendSessionRequest = () => {
// Try to get existing session_id, including preserved one during token refresh
let localId = this.getSessionId();
// If no session in localStorage but socket has preservedSessionId, use it
if (!localId && socket.preservedSessionId) {
localId = socket.preservedSessionId;
logger.debug('Using preserved session_id for token refresh:', localId);
}
logger.info('📤 Sending session_request', {
session_id: localId || 'null (requesting new)',
hasAuth: !!customData?.auth_header,
socketId: socket.socket?.id
});
// DIAGNOSTIC: Check token expiration before session_request
if (customData?.auth_header) {
try {
const tokenPayload = customData.auth_header.split('.')[1];
const decoded = JSON.parse(atob(tokenPayload.replace(/-/g, '+').replace(/_/g, '/')));
const now = Date.now() / 1000;
const timeLeft = decoded.exp - now;
logger.info('🔍 SESSION_REQUEST: Access token expires in:', Math.round(timeLeft / 60), 'minutes');
if (timeLeft < 0) {
logger.error('❌ SESSION_REQUEST: Using EXPIRED access token! Expired', Math.round(-timeLeft / 60), 'minutes ago');
}
} catch (e) {
logger.error('❌ SESSION_REQUEST: Failed to decode access token:', e);
}
}
// Only include session_id if we have one, otherwise let backend create new one
const payload = { customData };
if (localId) {
payload.session_id = localId;
}
socket.emit('session_request', payload);
};
socket.on('connect', sendSessionRequest);
// CRITICAL: If socket is already connected, send session_request immediately
if (socket.isInitialized()) {
logger.info('🔄 Socket already connected, sending session_request immediately');
sendSessionRequest();
}
// Register session_confirm handler
socket.on('session_confirm', (sessionObject) => {
const remoteId = (sessionObject && sessionObject.session_id)
? sessionObject.session_id
: sessionObject;
logger.info(`session_confirm:${socket.socket.id} session_id:${remoteId}`);
// Store the initial state to both the redux store and the storage, set connected to true
dispatch(connectServer());
let localId = this.getSessionId();
// If we were trying to preserve a session_id during token refresh
if (!localId && socket.preservedSessionId) {
localId = socket.preservedSessionId;
logger.info(`Token refresh: requested preserved session_id ${localId}, server returned ${remoteId}`);
}
if (localId !== remoteId) {
// Store the received session_id to storage
storeLocalSession(storage, SESSION_NAME, remoteId);
dispatch(pullSession());
if (sendInitPayload) {
this.trySendInitPayload();
}
} else {
logger.info('Session_id preserved successfully during token refresh');
// If this is an existing session, it's possible we changed pages and want to send a
// user message when we land.
const nextMessage = window.localStorage.getItem(NEXT_MESSAGE);
if (nextMessage !== null) {
const { message, expiry } = JSON.parse(nextMessage);
window.localStorage.removeItem(NEXT_MESSAGE);
if (expiry === 0 || expiry > Date.now()) {
dispatch(addUserMessage(message));
dispatch(emitUserMessage(message));
dispatch(setBotProcessing(true));
// Start 30-second timeout to reset bot processing if backend hangs
startBotProcessingTimeout(dispatch);
}
}
}
// Clear preserved session_id after use
if (socket.preservedSessionId) {
delete socket.preservedSessionId;
}
if (connectOn === 'mount' && tooltipPayload) {
this.tooltipTimeout = setTimeout(() => {
this.trySendTooltipPayload();
}, parseInt(tooltipDelay, 10));
}
});
// Register disconnect handler
socket.on('disconnect', (reason) => {
logger.info('Disconnected:', reason);
if (reason !== 'io client disconnect') {
dispatch(disconnectServer());
}
});
// Mark handlers as registered
this.socketHandlersRegistered = true;
logger.info('✅ Socket event handlers registered');
}