// Initialize chat when document is ready $(document).ready(function() { const conversationId = window.conversationId; // Set this in the template const currentUserId = window.currentUserId; // Set this in the template const chat = ChatManager.getInstance(conversationId); const socket = chat.socket; const state = chat.state; console.log('Initializing chat for conversation:', conversationId); // Join conversation room socket.on('connect', function() { if (!state.connectionState.hasJoined) { console.log('Joining conversation room:', conversationId); socket.emit('join_conversation', { conversation_id: conversationId, timestamp: new Date().toISOString(), socketId: socket.id }); state.connectionState.hasJoined = true; } }); // Function to append a new message to the chat function appendMessage(message) { const isCurrentUser = message.sender_id === currentUserId; const messageHtml = `
`; // Remove the "no messages" placeholder if it exists $('.text-center.text-muted').remove(); $('#chatMessages').append(messageHtml); scrollToBottom(); } // Scroll to bottom of chat messages function scrollToBottom() { const chatMessages = document.getElementById('chatMessages'); chatMessages.scrollTop = chatMessages.scrollHeight; } scrollToBottom(); // Message handling with deduplication and reconnection handling socket.on('new_message', function(message) { const timestamp = new Date().toISOString(); const messageKey = `${message.id}-${socket.id}`; console.log('Message received:', { id: message.id, timestamp: timestamp, socketId: socket.id, messageKey: messageKey, queueSize: state.messageQueue.size }); if (state.messageQueue.has(messageKey)) { console.log('Message already in queue:', messageKey); return; } state.messageQueue.add(messageKey); if (!state.addedMessageIds.has(message.id)) { console.log('Processing new message:', message.id); appendMessage(message); state.connectionState.lastMessageId = message.id; state.addedMessageIds.add(message.id); } else { console.log('Duplicate message detected:', { messageId: message.id, lastMessageId: state.connectionState.lastMessageId, socketId: socket.id }); } // Clean up message from queue after processing state.messageQueue.delete(messageKey); }); // Handle file selection $('#fileInput').on('change', function() { const files = Array.from(this.files); if (files.length > 0) { const fileNames = files.map(file => file.name).join(', '); $('#selectedFiles').text(files.length > 1 ? `${files.length} files selected: ${fileNames}` : fileNames); } else { $('#selectedFiles').text(''); } }); // Handle message form submission with better error handling let isSubmitting = false; $('#messageForm').off('submit').on('submit', function(e) { e.preventDefault(); e.stopPropagation(); if (isSubmitting) { console.log('Message submission already in progress'); return false; } const messageInput = $('#messageInput'); const submitButton = $('#messageForm button[type="submit"]'); const submitIcon = submitButton.find('.fa-paper-plane'); const spinner = submitButton.find('.fa-circle-notch'); const message = messageInput.val().trim(); const fileInput = $('#fileInput')[0]; const files = Array.from(fileInput.files); if (!message && files.length === 0) { console.log('Empty message submission attempted'); return false; } console.log('Submitting message:', { hasText: !!message, fileCount: files.length, socketId: socket.id, connectionState: state.connectionState }); isSubmitting = true; messageInput.prop('disabled', true); submitButton.prop('disabled', true); submitIcon.addClass('d-none'); spinner.removeClass('d-none'); const formData = new FormData(); formData.append('message', message); formData.append('csrf_token', $('input[name="csrf_token"]').val()); formData.append('socket_id', socket.id); files.forEach((file, index) => { formData.append(`file_${index}`, file); }); formData.append('file_count', files.length); $.ajax({ url: window.sendMessageUrl, // Set this in the template method: 'POST', data: formData, processData: false, contentType: false, success: function(response) { console.log('Message sent successfully:', { response: response, socketId: socket.id }); if (response.success) { messageInput.val(''); fileInput.value = ''; $('#selectedFiles').text(''); // If socket is disconnected, append message directly if (!state.connectionState.isConnected && response.message) { console.log('Socket disconnected, appending message directly'); appendMessage(response.message); } } else { console.error('Message send failed:', response); alert('Failed to send message. Please try again.'); } }, error: function(xhr, status, error) { console.error('Failed to send message:', { status: status, error: error, response: xhr.responseText }); alert('Failed to send message. Please try again.'); }, complete: function() { messageInput.prop('disabled', false); submitButton.prop('disabled', false); submitIcon.removeClass('d-none'); spinner.addClass('d-none'); isSubmitting = false; } }); return false; }); // Clean up on page unload $(window).on('beforeunload', function() { chat.cleanup(); }); });