223 lines
8.5 KiB
JavaScript
223 lines
8.5 KiB
JavaScript
// 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 = `
|
|
<div class="message ${isCurrentUser ? 'sent' : 'received'}" data-message-id="${message.id}">
|
|
${!isCurrentUser ? `
|
|
<img src="${message.sender_avatar}"
|
|
alt="${message.sender_name}"
|
|
class="rounded-circle me-2"
|
|
style="width: 40px; height: 40px; object-fit: cover;">
|
|
` : ''}
|
|
<div class="message-content">
|
|
<div class="message-info">
|
|
<span class="fw-medium">${message.sender_name}</span>
|
|
<span class="text-muted ms-2">${message.created_at}</span>
|
|
</div>
|
|
${message.content}
|
|
${message.attachments && message.attachments.length > 0 ? `
|
|
<div class="attachments mt-2">
|
|
${message.attachments.map((attachment, index) => `
|
|
<div class="attachment mb-1">
|
|
<a href="${attachment.url}" class="btn btn-sm">
|
|
<i class="fas fa-paperclip me-1"></i>
|
|
${attachment.name}
|
|
<small class="ms-1">(${Math.round(attachment.size / 1024)} KB)</small>
|
|
</a>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
${isCurrentUser ? `
|
|
<img src="${message.sender_avatar}"
|
|
alt="${message.sender_name}"
|
|
class="rounded-circle ms-2"
|
|
style="width: 40px; height: 40px; object-fit: cover;">
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
|
|
// 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();
|
|
});
|
|
});
|