document.addEventListener('DOMContentLoaded', function () { let currentRoomId = null; let lastMessageId = 0; let messagePollInterval = null; let roomPollInterval = null; let isFetchingMessages = false; const UI = { roomList: document.getElementById('room-list'), noChatSelected: document.getElementById('no-chat-selected'), chatHeader: document.getElementById('active-chat-header'), chatTitle: document.getElementById('active-chat-title'), chatHeaderInfo: document.getElementById('chat-header-info'), messages: document.getElementById('chat-messages'), inputArea: document.getElementById('chat-input-area'), form: document.getElementById('chat-form'), input: document.getElementById('chat-input'), indicator: document.getElementById('polling-indicator'), // 🟢 متغیرهای جدید برای Modal کاربران newChatBtn: document.getElementById('new-chat-btn'), usersModal: document.getElementById('users-modal'), closeModalBtn: document.getElementById('close-users-modal'), usersList: document.getElementById('users-list'), searchInput: document.getElementById('user-search-input') }; // ── منطق مودال کاربران ── if (UI.newChatBtn) { UI.newChatBtn.addEventListener('click', () => { UI.usersModal.style.transform = 'translateY(0)'; // نمایش مودال (بالا آمدن) fetchUsers(''); // لود اولیه لیست کاربران }); } if (UI.closeModalBtn) { UI.closeModalBtn.addEventListener('click', () => { UI.usersModal.style.transform = 'translateY(100%)'; // مخفی کردن مودال (پایین رفتن) }); } if (UI.searchInput) { let timeout = null; UI.searchInput.addEventListener('input', (e) => { clearTimeout(timeout); // دی‌باونس (Debounce) برای جلوگیری از ریکوئست‌های تکراری timeout = setTimeout(() => { fetchUsers(e.target.value); }, 300); }); } async function fetchUsers(query) { UI.usersList.innerHTML = '
autorenew
'; try { const res = await fetch(`${window.CHAT_CONFIG.apiUrlUsers}?q=${encodeURIComponent(query)}`); const data = await res.json(); renderUsers(data.users); } catch (e) { console.error("Failed to fetch users:", e); UI.usersList.innerHTML = `

${window.CHAT_CONFIG.trans.failedToLoadUsers}

`; } } function renderUsers(users) { if (users.length === 0) { UI.usersList.innerHTML = `

${window.CHAT_CONFIG.trans.noUsersFound}

`; return; } UI.usersList.innerHTML = users.map(user => { const initial = user.name ? user.name.charAt(0).toUpperCase() : 'U'; // استفاده از کلاس‌های room-item و room-avatar برای هماهنگی 100 درصدی return `
${initial}
${user.name}
${user.role}
`; }).join(''); } window.startChatWithUser = async function (userId) { // بستن مودال بلافاصله بعد از کلیک UI.usersModal.style.transform = 'translateY(100%)'; try { const res = await fetch(window.CHAT_CONFIG.apiUrlCreateRoom, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': window.CHAT_CONFIG.csrfToken }, body: JSON.stringify({ user_id: userId }) }); const data = await res.json(); if (data.success) { // اگر با موفقیت ساخته یا پیدا شد، مستقیماً وارد چت می‌شویم selectRoom(data.room_id, data.title); } } catch (e) { console.error("Failed to start chat:", e); } }; // ── پایان منطق مودال کاربران ── // ── بقیه کدهای قبلی (fetchRooms, renderRooms, fetchMessages و...) بدون تغییر در اینجا قرار دارند ── async function fetchRooms() { try { const res = await fetch(window.CHAT_CONFIG.apiUrlRooms); const data = await res.json(); renderRooms(data.rooms); } catch (e) { console.error("Failed to fetch rooms:", e); } } function renderRooms(rooms) { if (rooms.length === 0) { UI.roomList.innerHTML = `

${window.CHAT_CONFIG.trans.noActiveChatsFound}

`; return; } UI.roomList.innerHTML = rooms.map(room => { const isActive = currentRoomId === room.id; const safeTitle = room.title.replace(/'/g, "\\'"); const initial = room.title ? room.title.charAt(0).toUpperCase() : 'G'; return `
${initial}
${room.title} ${room.time}
${room.type === 'group' ? '👥 ' + window.CHAT_CONFIG.trans.groupChat : '👤 ' + window.CHAT_CONFIG.trans.privateChat} ${room.unread > 0 ? `${room.unread}` : ''}
`; }).join(''); } window.selectRoom = function (id, title) { currentRoomId = id; lastMessageId = 0; isFetchingMessages = false; // 🟢 Optimistic UI: Update active state and remove unread badge immediately document.querySelectorAll('.room-item').forEach(el => el.classList.remove('room-item-active')); const clickedRoom = document.querySelector(`.room-item[onclick*="selectRoom(${id}"]`); if (clickedRoom) { clickedRoom.classList.add('room-item-active'); const badge = clickedRoom.querySelector('.unread-badge'); if (badge) badge.remove(); // Hide badge instantly } // 🟢 تغییر invisible به hidden برای کارکرد درست بلوک‌ها UI.noChatSelected.classList.add('hidden'); UI.chatHeaderInfo.classList.remove('hidden'); // 🟢 تغییر یافته UI.messages.classList.remove('hidden'); UI.inputArea.classList.remove('hidden'); UI.chatTitle.textContent = title; UI.messages.innerHTML = '
autorenew
'; stopPolling(); fetchMessages().then(() => { startPolling(); scrollToBottom(); fetchRooms(); // Sync with backend to ensure exact unread counts }); UI.input.focus(); }; async function fetchMessages() { if (!currentRoomId || isFetchingMessages) return; isFetchingMessages = true; try { UI.indicator.classList.remove('hidden'); setTimeout(() => UI.indicator.classList.add('hidden'), 500); const res = await fetch(`${window.CHAT_CONFIG.apiUrlMessagesBase}${currentRoomId}/?last_id=${lastMessageId}`); const data = await res.json(); if (data.messages && data.messages.length > 0) { if (lastMessageId === 0) UI.messages.innerHTML = ''; renderMessages(data.messages); lastMessageId = data.messages[data.messages.length - 1].id; scrollToBottom(); } else if (lastMessageId === 0) { UI.messages.innerHTML = `
chat

${window.CHAT_CONFIG.trans.noMessagesYet}

`; } } catch (e) { console.error("Failed to fetch messages:", e); } finally { isFetchingMessages = false; } } function renderMessages(messages) { const html = messages.map(m => { const isMe = m.is_me; const alignClass = isMe ? 'message-sent' : 'message-received'; const bubbleClass = isMe ? 'message-bubble-sent' : 'message-bubble-received'; const senderName = !isMe ? `
${m.sender_name}
` : ''; const safeContent = m.content.replace(//g, ">"); const timeColor = isMe ? 'text-blue-100' : 'text-gray-400'; const tickIcon = isMe ? `done_all` : ''; return `
${senderName}

${safeContent}

${m.sent_at} ${tickIcon}
`; }).join(''); UI.messages.insertAdjacentHTML('beforeend', html); } function scrollToBottom() { UI.messages.scrollTo({ top: UI.messages.scrollHeight, behavior: 'smooth' }); } // ── Textarea Auto-resize & Key Handling ── UI.input.addEventListener('input', function () { this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px'; }); UI.input.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); // Trigger form submit UI.form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true })); } }); UI.form.addEventListener('submit', async (e) => { e.preventDefault(); const text = UI.input.value.trim(); if (!text || !currentRoomId) return; UI.input.value = ''; UI.input.style.height = 'auto'; // Reset height try { await fetch(`${window.CHAT_CONFIG.apiUrlSendBase}${currentRoomId}/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': window.CHAT_CONFIG.csrfToken }, body: JSON.stringify({ content: text }) }); await fetchMessages(); fetchRooms(); } catch (e) { console.error("Failed to send message:", e); } }); function startPolling() { messagePollInterval = setInterval(fetchMessages, 3000); } function stopPolling() { if (messagePollInterval) clearInterval(messagePollInterval); } fetchRooms(); roomPollInterval = setInterval(fetchRooms, 10000); });