/** * @fileoverview Manages the real-time conversation functionality. * This file handles: * - Chat message display and management * - Message submission with file attachments * - Real-time message updates * - Chat state management * - UI interactions and animations */ /** * Initializes the chat functionality when the document is ready. * Sets up: * - Chat state and message tracking * - Message display and submission * - File attachment handling * - Real-time message updates * - Cleanup on page unload * @function */ $(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 state = chat.state; console.log('[Conversation] Initializing chat for conversation:', conversationId); // Keep track of messages we've already displayed const displayedMessageIds = new Set(); /** * Appends a new message to the chat interface. * Handles message formatting, attachments, and UI updates. * @function * @param {Object} message - The message object to append * @param {string} message.id - The unique message ID * @param {string} message.content - The message content * @param {string} message.sender_id - The ID of the message sender * @param {string} message.sender_name - The name of the message sender * @param {string} message.sender_avatar - The avatar URL of the sender * @param {string} message.created_at - The message creation timestamp * @param {Array} [message.attachments] - Array of file attachments */ function appendMessage(message) { console.log('[Conversation] Attempting to append message:', { messageId: message.id, content: message.content, senderId: message.sender_id, currentUserId: currentUserId }); // Check if we've already displayed this message if (displayedMessageIds.has(message.id)) { return; } displayedMessageIds.add(message.id); const isCurrentUser = message.sender_id === currentUserId; const messageHtml = `
`; // Remove the "no messages" placeholder if it exists $('.text-center.text-muted').remove(); $('#chatMessages').append(messageHtml); console.log('[Conversation] Message appended to chat:', message.id); scrollToBottom(); } // Initialize displayedMessageIds with existing messages $('.message').each(function() { const messageId = $(this).data('message-id'); if (messageId) { displayedMessageIds.add(messageId); } }); /** * Scrolls the chat window to the bottom. * @function */ function scrollToBottom() { const chatMessages = document.getElementById('chatMessages'); chatMessages.scrollTop = chatMessages.scrollHeight; } scrollToBottom(); /** * Event handler for new messages. * Appends new messages to the chat when received. * @event * @param {Event} event - The event object * @param {Object} message - The new message object */ $(document).on('new_message', function(event, message) { console.log('[Conversation] Received new_message event:', { messageId: message.id, eventType: event.type, timestamp: new Date().toISOString() }); appendMessage(message); }); /** * Handles file selection for message attachments. * Updates the UI to show selected files. * @event */ $('#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(''); } }); /** * Handles message form submission. * Processes text messages and file attachments. * Manages submission state and UI feedback. * @event * @param {Event} e - The form submission event * @returns {boolean} false to prevent default form submission */ let isSubmitting = false; $('#messageForm').off('submit').on('submit', function(e) { e.preventDefault(); e.stopPropagation(); if (isSubmitting) { console.log('[Conversation] 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('[Conversation] Empty message submission attempted'); return false; } console.log('[Conversation] Submitting message:', { hasText: !!message, fileCount: files.length, timestamp: new Date().toISOString() }); 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('file_count', files.length); files.forEach((file, index) => { formData.append(`file_${index}`, file); }); $.ajax({ url: window.sendMessageUrl, method: 'POST', data: formData, processData: false, contentType: false, success: function(response) { console.log('[Conversation] Message sent successfully:', { response: response, timestamp: new Date().toISOString() }); if (response.success) { messageInput.val(''); fileInput.value = ''; $('#selectedFiles').text(''); // Append the message directly since we sent it if (response.message) { console.log('[Conversation] Appending sent message directly:', response.message.id); // Update the ChatManager's lastMessageId chat.state.connectionState.lastMessageId = response.message.id; appendMessage(response.message); } } else { console.error('[Conversation] Message send failed:', response); alert('Failed to send message. Please try again.'); } }, error: function(xhr, status, error) { console.error('[Conversation] Failed to send message:', { status: status, error: error, response: xhr.responseText, timestamp: new Date().toISOString() }); 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; console.log('[Conversation] Message submission completed'); } }); return false; }); /** * Cleans up chat resources when the page is unloaded. * @event */ $(window).on('beforeunload', function() { console.log('[Conversation] Cleaning up on page unload'); chat.cleanup(); }); });