// Chat functionality let conversations = []; let messages = {}; let pollingInterval; // Mobile sidebar functionality function toggleMobileSidebar() { const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('sidebarOverlay'); if (sidebar && overlay) { sidebar.classList.toggle('show'); overlay.classList.toggle('show'); // Prevent body scroll when sidebar is open if (sidebar.classList.contains('show')) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } } } // Close mobile sidebar when clicking outside function closeMobileSidebar() { const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('sidebarOverlay'); if (sidebar && overlay) { sidebar.classList.remove('show'); overlay.classList.remove('show'); document.body.style.overflow = ''; } } // Initialize chat functionality document.addEventListener('DOMContentLoaded', () => { if (!document.body.classList.contains('chat-page')) { return; } initializeChat(); }); async function initializeChat() { try { console.log('Initializing chat...'); // Clear any existing data conversations = []; messages = {}; window.currentConversation = null; // Load fresh data await loadConversations(); startPolling(); setupEventListeners(); console.log('Chat initialized successfully'); } catch (error) { console.error('Failed to initialize chat:', error); MainJS.showError('Failed to initialize chat'); } } function setupEventListeners() { // Message form const messageForm = document.getElementById('messageForm'); if (messageForm) { messageForm.addEventListener('submit', handleSendMessage); } // Private chat form const privateChatForm = document.getElementById('privateChatForm'); if (privateChatForm) { privateChatForm.addEventListener('submit', handleStartPrivateChat); } // Group chat form const groupChatForm = document.getElementById('groupChatForm'); if (groupChatForm) { groupChatForm.addEventListener('submit', handleCreateGroup); } } async function loadConversations() { try { const response = await MainJS.apiRequest('/api/conversations'); if (response.success) { conversations = response.conversations || []; renderConversations(); } else { console.warn('Failed to load conversations:', response.message); // Show empty state instead of error for unauthenticated users conversations = []; renderConversations(); } } catch (error) { console.error('Failed to load conversations:', error); conversations = []; renderConversations(); } } function renderConversations() { const conversationsList = document.getElementById('conversationsList'); if (!conversationsList) { console.error('Conversations list element not found'); return; } console.log('Rendering conversations:', conversations); // FORCE CLEAR the conversations list first conversationsList.innerHTML = ''; if (conversations.length === 0) { conversationsList.innerHTML = `

No conversations yet

Start a new chat to begin messaging
`; console.log('No conversations to display'); return; } conversationsList.innerHTML = conversations.map(conv => { const lastMessage = conv.last_message; const isActive = window.currentConversation === conv.id; return `
${conv.type === 'group' ? '' : conv.name[0].toUpperCase()} ${conv.type === 'private' && conv.online ? '
' : ''}
${MainJS.escapeHtml(conv.name)}
${lastMessage ? `${MainJS.formatTime(lastMessage.timestamp)}` : ''}
${lastMessage ? `
${conv.type === 'group' ? `${MainJS.escapeHtml(lastMessage.sender_name)}: ` : ''} ${MainJS.escapeHtml(lastMessage.content)}
` : '
No messages yet
'} ${conv.type === 'private' && !conv.online ? '
offline
' : ''}
`; }).join(''); } async function selectConversation(conversationId) { try { console.log('Selecting conversation:', conversationId); // Validate that conversation exists const conversation = conversations.find(c => c.id === conversationId); if (!conversation) { console.error('Conversation not found:', conversationId); MainJS.showError('Conversation not found. Please refresh and try again.'); return; } window.currentConversation = conversationId; // Update UI document.querySelectorAll('.conversation-item').forEach(item => { item.classList.remove('active'); }); // Find and activate the clicked conversation const clickedItem = document.querySelector(`[onclick*="${conversationId}"]`); if (clickedItem) { clickedItem.classList.add('active'); } // Show chat container const welcomeScreen = document.getElementById('welcomeScreen'); const chatContainer = document.getElementById('chatContainer'); if (welcomeScreen) welcomeScreen.style.display = 'none'; if (chatContainer) { chatContainer.style.display = 'flex'; console.log('Chat container displayed'); } else { console.error('Chat container not found'); } // Close mobile sidebar when conversation is selected if (window.innerWidth < 768) { closeMobileSidebar(); } // Update chat header first updateChatHeader(conversationId); // Load conversation details console.log('Loading messages for conversation:', conversationId); await loadMessages(conversationId); // Mark messages as seen markMessagesAsSeen(conversationId); console.log('Conversation selected successfully'); } catch (error) { console.error('Error selecting conversation:', error); MainJS.showError('Failed to load conversation'); } } async function loadMessages(conversationId) { try { console.log('Loading messages for conversation ID:', conversationId); const response = await MainJS.apiRequest(`/api/messages/${conversationId}`); console.log('Messages API response:', response); if (response.success) { messages[conversationId] = response.messages || []; console.log('Messages loaded:', response.messages.length); renderMessages(conversationId); } else { console.error('API error:', response.message); // Even if API fails, show the chat interface with empty state messages[conversationId] = []; renderMessages(conversationId); MainJS.showError('Failed to load messages: ' + response.message); } } catch (error) { console.error('Failed to load messages:', error); // Show empty chat interface even on error messages[conversationId] = []; renderMessages(conversationId); MainJS.showError('Connection error while loading messages'); } } function renderMessages(conversationId) { console.log('Rendering messages for conversation:', conversationId); const chatMessages = document.getElementById('chatMessages'); console.log('Chat messages element:', chatMessages); console.log('Messages data:', messages[conversationId]); if (!chatMessages) { console.error('Chat messages element not found!'); return; } if (!messages[conversationId]) { console.error('No messages found for conversation:', conversationId); return; } const conversationMessages = messages[conversationId]; console.log('Number of messages to render:', conversationMessages.length); if (conversationMessages.length === 0) { console.log('No messages, showing empty state'); chatMessages.innerHTML = `

No messages yet

Send the first message to start the conversation
`; return; } chatMessages.innerHTML = conversationMessages.map(msg => { const isCurrentUser = msg.sender_id === getCurrentUserId(); const messageClass = isCurrentUser ? 'sent' : 'received'; return `
${!isCurrentUser && getConversationType(conversationId) === 'group' ? `
${MainJS.escapeHtml(msg.sender_name)}
` : ''}
${MainJS.escapeHtml(msg.content)}
${MainJS.formatMessageTime(msg.timestamp)} ${isCurrentUser ? getMessageStatusIcon(msg) : ''}
`; }).join(''); // Scroll to bottom chatMessages.scrollTop = chatMessages.scrollHeight; } function getMessageStatusIcon(message) { const seenCount = message.seen_by ? message.seen_by.length : 0; const conversation = conversations.find(c => c.id === window.currentConversation); const totalParticipants = conversation ? conversation.participants.length : 1; if (seenCount >= totalParticipants) { return ''; } else if (seenCount > 1) { return ''; } else { return ''; } } function updateChatHeader(conversationId) { const chatHeader = document.getElementById('chatHeader'); const conversation = conversations.find(c => c.id === conversationId); if (!chatHeader || !conversation) return; chatHeader.innerHTML = `
${conversation.type === 'group' ? '' : conversation.name[0].toUpperCase()} ${conversation.type === 'private' && conversation.online ? '
' : ''}
${MainJS.escapeHtml(conversation.name)}
${conversation.type === 'group' ? `${conversation.participants.length} members` : conversation.online ? 'online' : 'offline' }
`; } async function handleSendMessage(event) { event.preventDefault(); if (!window.currentConversation) { MainJS.showError('Please select a conversation first'); return; } const messageInput = document.getElementById('messageInput'); const content = messageInput.value.trim(); if (!content) return; try { const response = await MainJS.apiRequest('/api/send_message', { method: 'POST', body: JSON.stringify({ conversation_id: window.currentConversation, content: content }) }); if (response.success) { messageInput.value = ''; // Add message to local state if (!messages[window.currentConversation]) { messages[window.currentConversation] = []; } messages[window.currentConversation].push(response.message); // Re-render messages renderMessages(window.currentConversation); // Update conversations list await loadConversations(); } else { throw new Error(response.message); } } catch (error) { console.error('Failed to send message:', error); MainJS.showError('Failed to send message'); } } async function markMessagesAsSeen(conversationId) { try { await MainJS.apiRequest('/api/mark_seen', { method: 'POST', body: JSON.stringify({ conversation_id: conversationId }) }); } catch (error) { console.error('Failed to mark messages as seen:', error); } } function startPolling() { // Poll for new messages every 2 seconds pollingInterval = setInterval(async () => { try { // Reload conversations to get latest messages await loadConversations(); // If a conversation is selected, reload its messages if (window.currentConversation) { await loadMessages(window.currentConversation); markMessagesAsSeen(window.currentConversation); } } catch (error) { console.error('Polling error:', error); } }, 2000); } function stopPolling() { if (pollingInterval) { clearInterval(pollingInterval); pollingInterval = null; } } // Modal functions function startPrivateChat() { const modal = new bootstrap.Modal(document.getElementById('newChatModal')); modal.hide(); setTimeout(() => { const privateChatModal = new bootstrap.Modal(document.getElementById('privateChatModal')); privateChatModal.show(); // Reset form document.getElementById('privateChatForm').reset(); document.getElementById('userPreview').style.display = 'none'; document.getElementById('startChatBtn').style.display = 'none'; window.foundUser = null; }, 300); } function startGroupChat() { const modal = new bootstrap.Modal(document.getElementById('newChatModal')); modal.hide(); setTimeout(() => { const groupChatModal = new bootstrap.Modal(document.getElementById('groupChatModal')); groupChatModal.show(); // Reset form document.getElementById('groupChatForm').reset(); document.getElementById('groupMembers').innerHTML = `
`; }, 300); } async function findUser() { const userIdInput = document.getElementById('userIdInput'); const uniqueId = userIdInput.value.trim().toUpperCase(); if (!uniqueId) { MainJS.showError('Please enter a user ID'); return; } try { const response = await MainJS.apiRequest('/api/find_user', { method: 'POST', body: JSON.stringify({ unique_id: uniqueId }) }); if (response.success) { window.foundUser = response.user; const userPreview = document.getElementById('userPreview'); userPreview.innerHTML = `
${response.user.name[0].toUpperCase()}
${MainJS.escapeHtml(response.user.name)}
${response.user.unique_id}
${response.user.online ? 'online' : 'offline'}
`; userPreview.style.display = 'block'; document.getElementById('startChatBtn').style.display = 'block'; } else { MainJS.showError(response.message); document.getElementById('userPreview').style.display = 'none'; document.getElementById('startChatBtn').style.display = 'none'; window.foundUser = null; } } catch (error) { console.error('Failed to find user:', error); MainJS.showError('Failed to find user'); } } async function handleStartPrivateChat(event) { event.preventDefault(); if (!window.foundUser) { MainJS.showError('Please find a user first'); return; } try { const response = await MainJS.apiRequest('/api/start_conversation', { method: 'POST', body: JSON.stringify({ type: 'private', target_user_id: window.foundUser.user_id }) }); if (response.success) { // Close modal const modal = bootstrap.Modal.getInstance(document.getElementById('privateChatModal')); modal.hide(); // Reload conversations await loadConversations(); // Select the new conversation selectConversation(response.conversation_id); MainJS.showSuccess('Private chat started'); } else { throw new Error(response.message); } } catch (error) { console.error('Failed to start private chat:', error); MainJS.showError('Failed to start private chat'); } } function addMemberField() { const groupMembers = document.getElementById('groupMembers'); const memberInputs = groupMembers.querySelectorAll('.member-input'); if (memberInputs.length >= 9) { MainJS.showError('Maximum 9 members allowed'); return; } const newField = document.createElement('div'); newField.className = 'input-group mb-2'; newField.innerHTML = ` `; groupMembers.appendChild(newField); } function removeMemberField(button) { button.closest('.input-group').remove(); } async function handleCreateGroup(event) { event.preventDefault(); const groupNameInput = document.getElementById('groupNameInput'); const groupName = groupNameInput.value.trim(); if (!groupName) { MainJS.showError('Please enter a group name'); return; } const memberInputs = document.querySelectorAll('.member-input'); const memberIds = Array.from(memberInputs) .map(input => input.value.trim().toUpperCase()) .filter(id => id.length > 0); if (memberIds.length < 2) { MainJS.showError('Please add at least 2 members'); return; } if (memberIds.length > 9) { MainJS.showError('Maximum 9 members allowed'); return; } try { // Find all users first to validate const userPromises = memberIds.map(id => MainJS.apiRequest('/api/find_user', { method: 'POST', body: JSON.stringify({ unique_id: id }) }) ); const userResults = await Promise.all(userPromises); const validUsers = []; for (let i = 0; i < userResults.length; i++) { if (userResults[i].success) { validUsers.push(userResults[i].user.user_id); } else { MainJS.showError(`User ${memberIds[i]} not found`); return; } } // Create group const response = await MainJS.apiRequest('/api/start_conversation', { method: 'POST', body: JSON.stringify({ type: 'group', group_name: groupName, participant_ids: validUsers }) }); if (response.success) { // Close modal const modal = bootstrap.Modal.getInstance(document.getElementById('groupChatModal')); modal.hide(); // Reload conversations await loadConversations(); // Select the new conversation selectConversation(response.conversation_id); MainJS.showSuccess('Group created successfully'); } else { throw new Error(response.message); } } catch (error) { console.error('Failed to create group:', error); MainJS.showError('Failed to create group'); } } // Helper functions function getCurrentUserId() { return window.currentUserId || null; } function getConversationType(conversationId) { const conversation = conversations.find(c => c.id === conversationId); return conversation ? conversation.type : 'private'; } // Cleanup on page unload window.addEventListener('beforeunload', () => { stopPolling(); });