whats-app / attached_assets /chat_1753191982107.js
abdullahalioo's picture
Upload 38 files
f767094 verified
// 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 = `
<div class="text-center p-4 text-muted">
<i class="fas fa-comments mb-3" style="font-size: 2rem;"></i>
<p>No conversations yet</p>
<small>Start a new chat to begin messaging</small>
</div>
`;
console.log('No conversations to display');
return;
}
conversationsList.innerHTML = conversations.map(conv => {
const lastMessage = conv.last_message;
const isActive = window.currentConversation === conv.id;
return `
<div class="conversation-item ${isActive ? 'active' : ''}" onclick="selectConversation('${conv.id}')">
<div class="d-flex align-items-center">
<div class="avatar bg-success me-3 position-relative">
${conv.type === 'group' ? '<i class="fas fa-users"></i>' : conv.name[0].toUpperCase()}
${conv.type === 'private' && conv.online ? '<div class="online-indicator"></div>' : ''}
</div>
<div class="flex-grow-1">
<div class="d-flex justify-content-between align-items-start">
<div class="fw-bold">${MainJS.escapeHtml(conv.name)}</div>
${lastMessage ? `<small class="text-muted">${MainJS.formatTime(lastMessage.timestamp)}</small>` : ''}
</div>
${lastMessage ? `
<div class="text-muted small text-truncate">
${conv.type === 'group' ? `${MainJS.escapeHtml(lastMessage.sender_name)}: ` : ''}
${MainJS.escapeHtml(lastMessage.content)}
</div>
` : '<div class="text-muted small">No messages yet</div>'}
${conv.type === 'private' && !conv.online ? '<div class="text-offline small">offline</div>' : ''}
</div>
</div>
</div>
`;
}).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 = `
<div class="text-center text-muted">
<i class="fas fa-comment-dots mb-2" style="font-size: 2rem;"></i>
<p>No messages yet</p>
<small>Send the first message to start the conversation</small>
</div>
`;
return;
}
chatMessages.innerHTML = conversationMessages.map(msg => {
const isCurrentUser = msg.sender_id === getCurrentUserId();
const messageClass = isCurrentUser ? 'sent' : 'received';
return `
<div class="message-item ${messageClass}">
<div class="message-content">
${!isCurrentUser && getConversationType(conversationId) === 'group' ?
`<div class="small text-muted mb-1">${MainJS.escapeHtml(msg.sender_name)}</div>` : ''}
<div class="message-bubble">
${MainJS.escapeHtml(msg.content)}
</div>
<div class="message-time">
${MainJS.formatMessageTime(msg.timestamp)}
${isCurrentUser ? getMessageStatusIcon(msg) : ''}
</div>
</div>
</div>
`;
}).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 '<span class="message-status seen"><i class="fas fa-check-double"></i></span>';
} else if (seenCount > 1) {
return '<span class="message-status delivered"><i class="fas fa-check-double"></i></span>';
} else {
return '<span class="message-status sent"><i class="fas fa-check"></i></span>';
}
}
function updateChatHeader(conversationId) {
const chatHeader = document.getElementById('chatHeader');
const conversation = conversations.find(c => c.id === conversationId);
if (!chatHeader || !conversation) return;
chatHeader.innerHTML = `
<div class="d-flex align-items-center">
<div class="avatar bg-success me-3 position-relative">
${conversation.type === 'group' ? '<i class="fas fa-users"></i>' : conversation.name[0].toUpperCase()}
${conversation.type === 'private' && conversation.online ? '<div class="online-indicator"></div>' : ''}
</div>
<div>
<div class="fw-bold">${MainJS.escapeHtml(conversation.name)}</div>
<small>
${conversation.type === 'group'
? `${conversation.participants.length} members`
: conversation.online ? 'online' : 'offline'
}
</small>
</div>
</div>
`;
}
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 = `
<div class="input-group mb-2">
<input type="text" class="form-control member-input" placeholder="Enter user ID">
<button type="button" class="btn btn-outline-success" onclick="addMemberField()">
<i class="fas fa-plus"></i>
</button>
</div>
`;
}, 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 = `
<div class="d-flex align-items-center">
<div class="avatar bg-success me-3">
${response.user.name[0].toUpperCase()}
</div>
<div>
<div class="fw-bold">${MainJS.escapeHtml(response.user.name)}</div>
<small class="text-muted">${response.user.unique_id}</small>
<div class="small ${response.user.online ? 'text-success' : 'text-muted'}">
${response.user.online ? 'online' : 'offline'}
</div>
</div>
</div>
`;
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 = `
<input type="text" class="form-control member-input" placeholder="Enter user ID">
<button type="button" class="btn btn-outline-danger" onclick="removeMemberField(this)">
<i class="fas fa-minus"></i>
</button>
`;
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();
});