import { config } from "./config.js"; import { waha } from "./waha.js"; import { ui, elements } from "./ui.js"; import { websocket } from "./websocket.js"; import { compensateMessageOrdering, formatTime } from "./utils.js"; let chatsState = []; let activeChatState = null; // Initialize app when DOM is ready document.addEventListener('DOMContentLoaded', () => { setupEventListeners(); fetchChats(); checkWahaStatus(); initWebSocket(); }); function setupEventListeners() { elements.refreshChatsBtn.addEventListener('click', () => { elements.refreshChatsBtn.classList.add('spinning'); fetchChats().finally(() => { setTimeout(() => { elements.refreshChatsBtn.classList.remove('spinning'); }, 600); }); }); elements.chatSearch.addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); const filtered = chatsState.filter(chat => chat.name.toLowerCase().includes(query) ); ui.renderChatList(filtered, activeChatState, selectChat); }); elements.messageForm.addEventListener('submit', (e) => { e.preventDefault(); sendMessage(); }); elements.backToSidebarBtn.addEventListener('click', () => { elements.sidebar.classList.remove('hidden'); elements.activeChatContainer.classList.add('hidden'); }); elements.settingsIconBtn.addEventListener('click', openSettings); elements.closeSettingsBtn.addEventListener('click', () => ui.toggleModal(false)); elements.cancelSettingsBtn.addEventListener('click', () => ui.toggleModal(false)); elements.saveSettingsBtn.addEventListener('click', saveSettings); } function initWebSocket() { websocket.connect((data) => { console.log('[WS] Received event:', data.event, data); const ev = data.event; if (ev === 'message' || ev === 'message.any' || ev === 'message.ack') { handleIncomingMessage(data.payload); } }); } function normalizeChatId(raw) { if (!raw) return null; if (typeof raw === 'object') { return raw._serialized || raw.user || JSON.stringify(raw); } return raw; } function handleIncomingMessage(msg) { if (!msg) return; console.log('[WS] handleIncomingMessage payload:', msg); const rawChatId = msg.chatId || msg.from || (msg.chat && msg.chat.id); const msgChatId = normalizeChatId(rawChatId); if (!msgChatId) { console.warn('[WS] Could not resolve chatId from payload:', msg); return; } console.log('[WS] Resolved msgChatId:', msgChatId, '| activeChatState:', activeChatState?.id); if (activeChatState && activeChatState.id === msgChatId) { const msgId = normalizeChatId(msg.id) || msg.id; const exists = document.getElementById(msgId); if (!exists) { ui.appendSingleMessage({ ...msg, chatId: msgChatId }, activeChatState.name); ui.scrollToBottom(); } } const chatIndex = chatsState.findIndex(c => c.id === msgChatId); if (chatIndex !== -1) { const chat = chatsState[chatIndex]; chat.lastMessage = msg.body || msg.text || 'Media message'; chat.timestamp = msg.timestamp ? (msg.timestamp * 1000) : Date.now(); if (!msg.fromMe && (!activeChatState || activeChatState.id !== msgChatId)) { chat.unreadCount = (chat.unreadCount || 0) + 1; } chatsState.sort((a, b) => { const tA = new Date(a.timestamp).getTime(); const tB = new Date(b.timestamp).getTime(); return tB - tA; }); ui.renderChatList(chatsState, activeChatState, selectChat); } else { fetchChats(); } } async function fetchChats() { const userInfo = await waha.getMyInfo(); elements.loggedUserName.textContent = userInfo.pushName; // elements.userIcon.innerText = userInfo.pushName[0]; elements.chatsLoader.classList.remove('hidden'); try { chatsState = await waha.getChats(); ui.renderChatList(chatsState, activeChatState, selectChat); } catch (error) { console.error('Failed to load chats:', error); elements.chatList.innerHTML = `
  • Connection to WAHA failed.

    Ensure WAHA server is running and CORS is enabled, or click Settings to configure.

  • `; } finally { elements.chatsLoader.classList.add('hidden'); } } async function selectChat(chat) { activeChatState = chat; chat.unreadCount = 0; ui.renderChatList(chatsState, activeChatState, selectChat); ui.toggleChatState(true); elements.activeChatName.textContent = chat.name.toUpperCase(); elements.activeChatAvatar.textContent = chat.name ? chat.name.substring(0, 1).toUpperCase() : '?'; elements.messagesContainer.innerHTML = '
    Loading messages...
    '; if (window.innerWidth <= 768) { elements.sidebar.classList.add('hidden'); } try { const rawMessages = await waha.getChatMessages(chat.id); const processedMessages = compensateMessageOrdering(rawMessages); ui.renderMessages(processedMessages, chat.name); } catch (error) { console.error('Failed to load messages:', error); elements.messagesContainer.innerHTML = '
    Error loading messages
    '; } } async function sendMessage() { const text = elements.messageInput.value.trim(); if (!text || !activeChatState) return; elements.messageInput.value = ''; const tempMsg = { id: 'temp-' + Date.now(), body: text, fromMe: true, sender: 'me', timestamp: new Date().toISOString(), status: 'sending' }; ui.appendSingleMessage(tempMsg, activeChatState.name); ui.scrollToBottom(); try { try { await waha.startTyping(activeChatState.id); const delay = Math.min(4000, Math.max(1000, text.length * 50)); await new Promise(resolve => setTimeout(resolve, delay)); } catch (e) { console.warn('Presence start failed:', e); } try { await waha.stopTyping(activeChatState.id); } catch (e) { console.warn('Presence stop failed:', e); } try { if (!activeChatState.id.endsWith('@lid')) { await waha.readChat(activeChatState.id); } } catch (e) { console.warn('readChat failed (non-fatal):', e.message); } const responseData = await waha.sendTextMessage(activeChatState.id, text); const tempBubble = document.getElementById(tempMsg.id); if (tempBubble) { if (responseData && responseData.id) { tempBubble.id = responseData.id; } const meta = tempBubble.querySelector('.message-meta'); meta.innerHTML = `${formatTime(new Date())}`; lucide.createIcons(); } activeChatState.lastMessage = text; activeChatState.timestamp = new Date(); chatsState.sort((a, b) => { const tA = new Date(a.timestamp).getTime(); const tB = new Date(b.timestamp).getTime(); return tB - tA; }); ui.renderChatList(chatsState, activeChatState, selectChat); } catch (error) { console.error('Failed to send message:', error); const tempBubble = document.getElementById(tempMsg.id); if (tempBubble) { const meta = tempBubble.querySelector('.message-meta'); meta.innerHTML = `Failed to send`; } } } function openSettings() { elements.inputWahaUrl.value = config.wahaUrl; elements.inputSession.value = config.session; elements.inputApiKey.value = config.apiKey; ui.toggleModal(true); } function saveSettings() { config.save( elements.inputWahaUrl.value, elements.inputSession.value, elements.inputApiKey.value ); ui.toggleModal(false); fetchChats(); checkWahaStatus(); initWebSocket(); } async function checkWahaStatus() { try { const data = await waha.getVersion(); ui.updateConnectionStatus(true, `WAHA Connected: v${data.version || 'OK'}`); } catch (e) { ui.updateConnectionStatus(false, 'WAHA Server Offline'); } }