abdullahalioo's picture
Upload 18 files
2f7626f 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 with local storage for instant loading
console.log('Loading messages for conversation:', conversationId);
await loadMessagesWithLocalStorage(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);
// Save messages to local storage
saveMessagesToLocalStorage(conversationId, messages[conversationId]);
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';
// Render different message types
if (msg.message_type === 'image') {
return renderImageMessage(msg, messageClass);
} else if (msg.message_type === 'file') {
return renderFileMessage(msg, messageClass);
} else if (msg.message_type === 'audio') {
return renderAudioMessage(msg, messageClass);
} else {
return renderTextMessage(msg, messageClass);
}
}).join('');
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
// Mark messages as seen when viewing conversation
setTimeout(() => markVisibleMessagesAsSeen(), 500);
}
function renderTextMessage(msg, messageClass) {
const isCurrentUser = messageClass === 'sent';
return `
<div class="message-item ${messageClass}">
<div class="message-content">
${!isCurrentUser && getConversationType(window.currentConversation) === '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>
`;
}
function renderImageMessage(msg, messageClass) {
const isCurrentUser = messageClass === 'sent';
return `
<div class="message-item ${messageClass}">
<div class="message-content">
${!isCurrentUser && getConversationType(window.currentConversation) === 'group' ?
`<div class="small text-muted mb-1">${MainJS.escapeHtml(msg.sender_name)}</div>` : ''}
<div class="image-message ${messageClass}" onclick="openImagePreview('${msg.id}')">
<img src="/api/image/${msg.id}" alt="${MainJS.escapeHtml(msg.file_name || 'Image')}"
class="message-image" loading="lazy">
<div class="image-overlay">
<i class="fas fa-search-plus"></i>
</div>
</div>
<div class="message-time">
${MainJS.formatMessageTime(msg.timestamp)}
${isCurrentUser ? getMessageStatusIcon(msg) : ''}
</div>
</div>
</div>
`;
}
function renderFileMessage(msg, messageClass) {
const isCurrentUser = messageClass === 'sent';
const iconClass = MainJS.getFileIconClass(msg.file_type || '');
const iconColor = MainJS.getFileIconColor(msg.file_type || '');
return `
<div class="message-item ${messageClass}">
<div class="message-content">
${!isCurrentUser && getConversationType(window.currentConversation) === 'group' ?
`<div class="small text-muted mb-1">${MainJS.escapeHtml(msg.sender_name)}</div>` : ''}
<div class="file-message ${messageClass}" onclick="downloadFile('${msg.id}')">
<div class="file-info">
<div class="file-icon ${iconColor}">
<i class="${iconClass}"></i>
</div>
<div class="file-details">
<div class="file-name">${MainJS.escapeHtml(msg.file_name || 'Unknown File')}</div>
<div class="file-size">${msg.file_size_formatted || '0B'}</div>
</div>
</div>
</div>
<div class="message-time">
${MainJS.formatMessageTime(msg.timestamp)}
${isCurrentUser ? getMessageStatusIcon(msg) : ''}
</div>
</div>
</div>
`;
}
function renderAudioMessage(msg, messageClass) {
const isCurrentUser = messageClass === 'sent';
const duration = msg.audio_duration ? Math.floor(msg.audio_duration) : 0;
const minutes = Math.floor(duration / 60);
const seconds = duration % 60;
const durationText = `${minutes}:${seconds.toString().padStart(2, '0')}`;
return `
<div class="message-item ${messageClass}">
<div class="message-content">
${!isCurrentUser && getConversationType(window.currentConversation) === 'group' ?
`<div class="small text-muted mb-1">${MainJS.escapeHtml(msg.sender_name)}</div>` : ''}
<div class="audio-message ${messageClass}">
<div class="audio-controls">
<button class="audio-play-btn" onclick="playAudioMessage('${msg.id}')">
<i class="fas fa-play"></i>
</button>
<div class="audio-waveform"></div>
<div class="audio-duration">${durationText}</div>
</div>
</div>
<div class="message-time">
${MainJS.formatMessageTime(msg.timestamp)}
${isCurrentUser ? getMessageStatusIcon(msg) : ''}
</div>
</div>
</div>
`;
}
function getMessageStatusIcon(message) {
// FIXED: Proper blue tick logic
// Blue ticks ONLY when recipient is ONLINE AND has actually seen the message
// Gray ticks when delivered but not seen OR when recipient is offline
const seenCount = message.seen_by ? message.seen_by.length : 0;
const currentUserId = document.querySelector('[data-user-id]')?.getAttribute('data-user-id');
// For group chats, check if any non-sender has seen it while online
const conversation = conversations.find(c => c.id === window.currentConversation);
if (!conversation) {
// Default to single gray tick if can't find conversation
return '<span class="message-status sent"><i class="fas fa-check"></i></span>';
}
// Check if ANY recipient is currently online AND has seen the message
const hasOnlineRecipientSeen = conversation.participants.some(participant => {
return participant.id !== currentUserId && // Not the sender
participant.online === true && // Currently online
message.seen_by && message.seen_by.includes(participant.id); // Has seen the message
});
if (hasOnlineRecipientSeen) {
// Blue double tick: seen by online recipient
return '<span class="message-status seen"><i class="fas fa-check-double"></i></span>';
} else if (seenCount > 0) {
// Gray double tick: seen but recipient was offline or is offline now
return '<span class="message-status delivered"><i class="fas fa-check-double"></i></span>';
} else {
// Single gray tick: delivered but not seen
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();
const messageInput = document.getElementById('messageInput');
const content = messageInput.value.trim();
if (!content || !window.currentConversation) {
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 for instant display (zero delay like your reference code)
if (!messages[window.currentConversation]) {
messages[window.currentConversation] = [];
}
messages[window.currentConversation].push(response.message);
// Save messages to local storage
saveMessagesToLocalStorage(window.currentConversation, messages[window.currentConversation]);
// Render messages instantly for fast response - no setTimeout delays
requestAnimationFrame(() => {
renderMessages(window.currentConversation);
});
// Update conversations list
await loadConversations();
} else {
MainJS.showError('Failed to send message: ' + response.message);
}
} catch (error) {
console.error('Error sending message:', error);
MainJS.showError('Failed to send message');
}
}
// File download function
async function downloadFile(messageId) {
try {
window.open(`/api/download/${messageId}`, '_blank');
} catch (error) {
console.error('Error downloading file:', error);
MainJS.showError('Failed to download file');
}
}
// Audio playback function
async function playAudioMessage(messageId) {
try {
const response = await fetch(`/api/download/${messageId}`);
if (response.ok) {
const blob = await response.blob();
const audioUrl = URL.createObjectURL(blob);
const audio = new Audio(audioUrl);
audio.play().catch(error => {
console.error('Error playing audio:', error);
MainJS.showError('Failed to play audio');
});
// Clean up URL when audio ends
audio.addEventListener('ended', () => {
URL.revokeObjectURL(audioUrl);
});
}
} catch (error) {
console.error('Error playing audio:', error);
MainJS.showError('Failed to play audio');
}
}
// Helper functions
function getCurrentUserId() {
return window.currentUserId;
}
function getConversationType(conversationId) {
const conversation = conversations.find(c => c.id === conversationId);
return conversation ? conversation.type : 'private';
}
async function markMessagesAsSeen(conversationId) {
try {
// Get all message IDs from current conversation
const conversationMessages = messages[conversationId] || [];
const messageIds = conversationMessages.map(msg => msg.id);
if (messageIds.length > 0) {
await MainJS.apiRequest('/api/mark_seen', {
method: 'POST',
body: JSON.stringify({
message_ids: messageIds
})
});
}
} catch (error) {
console.error('Failed to mark messages as seen:', error);
}
}
function startPolling() {
// Poll for new messages every 1 second for instant response like your original code
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);
}
}, 1000);
}
// New chat functions
function startPrivateChat() {
const newChatModal = bootstrap.Modal.getInstance(document.getElementById('newChatModal'));
const privateChatModal = new bootstrap.Modal(document.getElementById('privateChatModal'));
newChatModal.hide();
privateChatModal.show();
}
function startGroupChat() {
const newChatModal = bootstrap.Modal.getInstance(document.getElementById('newChatModal'));
const groupChatModal = new bootstrap.Modal(document.getElementById('groupChatModal'));
newChatModal.hide();
groupChatModal.show();
}
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 })
});
const userPreview = document.getElementById('userPreview');
const startChatBtn = document.getElementById('startChatBtn');
if (response.success) {
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>
</div>
`;
userPreview.style.display = 'block';
startChatBtn.style.display = 'block';
startChatBtn.dataset.userId = response.user.user_id;
} else {
userPreview.innerHTML = `<div class="text-danger">${response.message}</div>`;
userPreview.style.display = 'block';
startChatBtn.style.display = 'none';
}
} catch (error) {
console.error('Error finding user:', error);
MainJS.showError('Failed to find user');
}
}
async function handleStartPrivateChat(event) {
event.preventDefault();
const startChatBtn = document.getElementById('startChatBtn');
const userId = startChatBtn.dataset.userId;
if (!userId) {
MainJS.showError('Please find a user first');
return;
}
try {
const response = await MainJS.apiRequest('/api/start_private_chat', {
method: 'POST',
body: JSON.stringify({ user_id: userId })
});
if (response.success) {
const privateChatModal = bootstrap.Modal.getInstance(document.getElementById('privateChatModal'));
privateChatModal.hide();
// Refresh conversations and select the new one
await loadConversations();
await selectConversation(response.conversation_id);
} else {
MainJS.showError('Failed to start chat: ' + response.message);
}
} catch (error) {
console.error('Error starting private chat:', error);
MainJS.showError('Failed to start chat');
}
}
async function handleCreateGroup(event) {
event.preventDefault();
const groupName = document.getElementById('groupNameInput').value.trim();
const memberInputs = document.querySelectorAll('.member-input');
const members = Array.from(memberInputs)
.map(input => input.value.trim().toUpperCase())
.filter(value => value);
if (!groupName) {
MainJS.showError('Please enter a group name');
return;
}
if (members.length === 0) {
MainJS.showError('Please add at least one member');
return;
}
try {
const response = await MainJS.apiRequest('/api/create_group', {
method: 'POST',
body: JSON.stringify({
name: groupName,
members: members
})
});
if (response.success) {
const groupChatModal = bootstrap.Modal.getInstance(document.getElementById('groupChatModal'));
groupChatModal.hide();
// Reset form
document.getElementById('groupChatForm').reset();
// Refresh conversations and select the new one
await loadConversations();
await selectConversation(response.conversation_id);
} else {
MainJS.showError('Failed to create group: ' + response.message);
}
} catch (error) {
console.error('Error creating group:', error);
MainJS.showError('Failed to create group');
}
}
function addMemberField() {
const groupMembers = document.getElementById('groupMembers');
const memberCount = groupMembers.querySelectorAll('.member-input').length;
if (memberCount >= 9) {
MainJS.showError('Maximum 9 members allowed');
return;
}
const memberField = document.createElement('div');
memberField.className = 'input-group mb-2';
memberField.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(memberField);
}
function removeMemberField(button) {
button.parentElement.remove();
}
// Image preview functionality (WhatsApp-like) - SIMPLIFIED TO PREVENT TOUCH ISSUES
function openImagePreview(messageId) {
// Remove any existing image preview first
const existingModal = document.querySelector('.image-preview-modal');
if (existingModal) {
existingModal.remove();
}
const imageUrl = `/api/image/${messageId}`;
// Create simple modal with minimal interference
const modal = document.createElement('div');
modal.className = 'image-preview-modal';
modal.innerHTML = `
<div class="image-preview-overlay">
<div class="image-preview-container">
<button class="image-preview-close" onclick="closeImagePreview()">
<i class="fas fa-times"></i>
</button>
<img src="${imageUrl}" alt="Image Preview" class="image-preview-image">
<div class="image-preview-actions">
<a href="/api/download/${messageId}" target="_blank" class="btn btn-outline-light">
<i class="fas fa-download"></i> Download
</a>
</div>
</div>
</div>
`;
// Simple modal without any complex touch handling
document.body.appendChild(modal);
// Add click to close functionality
modal.querySelector('.image-preview-overlay').addEventListener('click', (e) => {
if (e.target === e.currentTarget) {
closeImagePreview();
}
});
// Mark message as seen when viewing image
markMessageAsSeen(messageId);
}
function closeImagePreview() {
// Simple close function - just remove modal, no complex touch handling
const modal = document.querySelector('.image-preview-modal');
if (modal) {
modal.remove();
console.log('Image preview closed');
}
}
// Touch event prevention function for modal
function preventTouch(e) {
e.preventDefault();
}
// Double blue tick system - Mark messages as seen
async function markMessageAsSeen(messageId) {
try {
await MainJS.apiRequest('/api/mark_seen', {
method: 'POST',
body: JSON.stringify({ message_ids: [messageId] })
});
} catch (error) {
console.error('Error marking message as seen:', error);
}
}
// Mark all visible messages as seen
async function markVisibleMessagesAsSeen() {
if (!window.currentConversation) return;
const conversationMessages = messages[window.currentConversation] || [];
const messageIds = conversationMessages
.filter(msg => msg.sender_id !== getCurrentUserId()) // Only mark messages from others
.map(msg => msg.id);
if (messageIds.length > 0) {
try {
await MainJS.apiRequest('/api/mark_seen', {
method: 'POST',
body: JSON.stringify({ message_ids: messageIds })
});
} catch (error) {
console.error('Error marking messages as seen:', error);
}
}
}
// Update message status (for double blue tick display)
async function updateMessageStatuses() {
if (!window.currentConversation) return;
const conversationMessages = messages[window.currentConversation] || [];
const currentUserId = getCurrentUserId();
// Only check status for messages sent by current user
const sentMessages = conversationMessages.filter(msg => msg.sender_id === currentUserId);
for (const message of sentMessages) {
try {
const response = await MainJS.apiRequest(`/api/message_status/${message.id}`);
if (response.success) {
message.status = response.status;
}
} catch (error) {
console.error('Error updating message status:', error);
}
}
}
// Local Storage Functions
function saveMessagesToLocalStorage(conversationId, messageList) {
try {
const storageKey = `chat_messages_${conversationId}`;
localStorage.setItem(storageKey, JSON.stringify(messageList));
console.log(`Saved ${messageList.length} messages to local storage for conversation ${conversationId}`);
} catch (error) {
console.error('Failed to save messages to local storage:', error);
}
}
function loadMessagesFromLocalStorage(conversationId) {
try {
const storageKey = `chat_messages_${conversationId}`;
const stored = localStorage.getItem(storageKey);
const messages = stored ? JSON.parse(stored) : [];
console.log(`Loaded ${messages.length} messages from local storage for conversation ${conversationId}`);
return messages;
} catch (error) {
console.error('Failed to load messages from local storage:', error);
return [];
}
}
function clearLocalStorageForConversation(conversationId) {
try {
const storageKey = `chat_messages_${conversationId}`;
localStorage.removeItem(storageKey);
} catch (error) {
console.error('Failed to clear local storage:', error);
}
}
// Load messages from local storage first, then update from server
async function loadMessagesWithLocalStorage(conversationId) {
try {
console.log('Loading messages with local storage for:', conversationId);
// First show cached messages for instant loading
const cachedMessages = loadMessagesFromLocalStorage(conversationId);
console.log('Cached messages loaded:', cachedMessages.length);
if (cachedMessages.length > 0) {
messages[conversationId] = cachedMessages;
renderMessages(conversationId);
console.log('Rendered cached messages');
}
// Then load fresh messages from server
console.log('Loading fresh messages from server...');
await loadMessages(conversationId);
} catch (error) {
console.error('Error in loadMessagesWithLocalStorage:', error);
// Fallback to regular loading
await loadMessages(conversationId);
}
}
// Mobile sidebar functions
function toggleMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
if (sidebar && overlay) {
sidebar.classList.toggle('show');
overlay.classList.toggle('show');
}
}
function closeMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
if (sidebar && overlay) {
sidebar.classList.remove('show');
overlay.classList.remove('show');
}
}
// Chat creation functions
function startPrivateChat() {
// Close new chat modal and open private chat modal
const newChatModal = bootstrap.Modal.getInstance(document.getElementById('newChatModal'));
const privateChatModal = new bootstrap.Modal(document.getElementById('privateChatModal'));
if (newChatModal) newChatModal.hide();
privateChatModal.show();
}
function startGroupChat() {
// Close new chat modal and open group chat modal
const newChatModal = bootstrap.Modal.getInstance(document.getElementById('newChatModal'));
const groupChatModal = new bootstrap.Modal(document.getElementById('groupChatModal'));
if (newChatModal) newChatModal.hide();
groupChatModal.show();
}
async function findUser() {
const userIdInput = document.getElementById('userIdInput');
const userPreview = document.getElementById('userPreview');
const startChatBtn = document.getElementById('startChatBtn');
const userId = userIdInput.value.trim();
if (!userId) {
MainJS.showError('Please enter a user ID');
return;
}
try {
const response = await MainJS.apiRequest('/api/find-user', {
method: 'POST',
body: JSON.stringify({ user_id: userId })
});
if (response.success && response.user) {
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">${MainJS.escapeHtml(response.user.email || 'No email')}</small>
</div>
</div>
`;
userPreview.style.display = 'block';
startChatBtn.style.display = 'block';
window.foundUser = response.user;
} else {
MainJS.showError('User not found');
userPreview.style.display = 'none';
startChatBtn.style.display = 'none';
}
} catch (error) {
console.error('Find user error:', error);
MainJS.showError('Failed to find user');
}
}
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
if (pollingInterval) {
clearInterval(pollingInterval);
}
});