From fe86db29875b9f0f295363934fd14a82566896a1 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 14 Jul 2026 19:10:59 -0300 Subject: [PATCH] Make generateMessage aware of temp messages, make sent attachements show on chat --- app.js | 84 +++++++++++++++++++++++++++++------------------ ui.js | 102 +++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 123 insertions(+), 63 deletions(-) diff --git a/app.js b/app.js index 184ac10..884091b 100644 --- a/app.js +++ b/app.js @@ -39,7 +39,7 @@ function setupEventListeners() { ui.toggleChatState(); } }); - + elements.refreshChatsBtn.addEventListener('click', () => { elements.refreshChatsBtn.classList.add('spinning'); fetchChats().finally(() => { @@ -48,7 +48,7 @@ function setupEventListeners() { }, 600); }); }); - + elements.chatSearch.addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); const filtered = chatsState.filter(chat => @@ -56,24 +56,24 @@ function setupEventListeners() { ); ui.renderChatList(filtered, activeChatState, selectChat); }); - + elements.messageForm.addEventListener('submit', (e) => { e.preventDefault(); sendMessage(); }); - + elements.chatBottomBar.style.height = `${elements.chatInputPanel.offsetHeight}px`; const observer = new ResizeObserver(() => { elements.chatBottomBar.style.height = - `${elements.chatInputPanel.offsetHeight}px`; + `${elements.chatInputPanel.offsetHeight}px`; }); - + observer.observe(elements.chatInputPanel); elements.chatBottomBarBtn.addEventListener('click', ui.toggleChatBottomBar); elements.chatBottomBar.addEventListener('click', (e) => { if (e.target == e.currentTarget) ui.toggleChatBottomBar(); }); - + elements.attachmentBtn.addEventListener('click', () => { elements.attachmentInput.click(); }) @@ -81,12 +81,12 @@ function setupEventListeners() { const firstFile = this.files[0]; sendFileMessage(firstFile); }) - + elements.backToSidebarBtn.addEventListener('click', () => { elements.sidebar.classList.remove('hidden'); elements.activeChatContainer.classList.add('hidden'); }); - + elements.settingsIconBtn.addEventListener('click', openSettings); elements.cancelSettingsBtn.addEventListener('click', () => ui.toggleModal(false)); elements.saveSettingsBtn.addEventListener('click', saveSettings); @@ -112,18 +112,18 @@ function normalizeChatId(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); @@ -132,23 +132,23 @@ function handleIncomingMessage(msg) { 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(); @@ -182,13 +182,13 @@ 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 = `
@@ -205,7 +205,7 @@ async function selectChat(chat) { if (window.innerWidth <= 768) { elements.sidebar.classList.add('hidden'); } - + try { const rawMessages = await waha.getChatMessages(chat.id); const processedMessages = compensateMessageOrdering(rawMessages); @@ -219,9 +219,9 @@ async function selectChat(chat) { async function sendMessage() { const text = elements.messageInput.value.trim(); if (!text || !activeChatState) return; - + elements.messageInput.value = ''; - + const tempMsg = { id: 'temp-' + Date.now(), body: text, @@ -230,10 +230,10 @@ async function sendMessage() { timestamp: new Date().toISOString(), status: 'sending' }; - + ui.appendSingleMessage(tempMsg, activeChatState.name, userInfo.id); ui.scrollToBottom(); - + try { try { await waha.startTyping(activeChatState.id); @@ -242,13 +242,13 @@ async function sendMessage() { } 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); @@ -256,9 +256,9 @@ async function sendMessage() { } 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) { @@ -267,7 +267,7 @@ async function sendMessage() { const meta = tempBubble.querySelector('.message-meta'); meta.innerHTML = `${formatTime(new Date())}`; } - + activeChatState.lastMessage = text; activeChatState.timestamp = new Date(); @@ -289,9 +289,29 @@ async function sendMessage() { async function sendFileMessage(file) { try { + const tempId = 'temp-' + Date.now(); + const tempMsg = { + _data: { + mimetype: file.type + }, + id: tempId, + body: "", + fromMe: true, + sender: 'me', + timestamp: new Date().toISOString(), + status: 'sending', + hasMedia: true, + media: { + url: URL.createObjectURL(file), + filename: file.name + } + }; + + ui.appendSingleMessage(tempMsg, activeChatState.name, userInfo.id, activeChatState.id, true); + ui.scrollToBottom(); + const result = await waha.sendFileMessage(activeChatState.id, file); - console.log(result) - // ui.appendSingleMessage(result, activeChatState.name, userInfo.id, activeChatState.id); + ui.updateMessageTick(tempId, result.status); } catch (error) { console.error(error.message); } diff --git a/ui.js b/ui.js index f680961..f852054 100644 --- a/ui.js +++ b/ui.js @@ -142,11 +142,29 @@ export const ui = { /** * Append a single message (used for optimistic updates immediately upon sending) */ - appendSingleMessage(msg, activeChatName, userID, chatId) { - elements.messagesContainer.appendChild(this.generateMessage(msg, activeChatName, userID, chatId)) + appendSingleMessage(msg, activeChatName, userID, chatId, isLocal = false) { + elements.messagesContainer.appendChild(this.generateMessage(msg, activeChatName, userID, chatId, isLocal)) }, - generateMessage(msg, activeChatName, userID, chatId) { + generateTempMessageLink(msg) { + const a = document.createElement('a'); + a.target = "_blank"; + a.href = msg.media.url; + + if (msg._data?.mimetype?.startsWith('image/')) { + const img = document.createElement('img'); + img.classList.add('message-image-attachement'); + img.src = msg.media.url; + a.appendChild(img); + } else { + a.textContent = msg.media.filename || "Download file"; + a.download = msg.media.filename; + } + + return a; + }, + + generateMessage(msg, activeChatName, userID, chatId, isLocal = false) { const isOutgoing = msg.fromMe || msg.sender === 'me'; const groupDiv = document.createElement('div'); @@ -187,43 +205,50 @@ export const ui = { contentEl.appendChild(textEl); bubble.appendChild(contentEl); - if (msg.hasMedia) { - const a = document.createElement('a'); - a.innerText = `[Request media]`; - a.target = "_blank"; + if (msg.hasMedia) { + let a; - const clickListener = async (e) => { - a.removeEventListener('click', clickListener); - a.innerText = `[Downloading]`; - const mediaMsg = await waha.getSingleChatMessage(chatId, msg.id, true); + if (isLocal) { + a = this.generateTempMessageLink(msg); + } else { + a = document.createElement('a'); + a.innerText = `[Request media]`; + a.target = "_blank"; - const url = new URL(mediaMsg.media.url); - const reqID = url.pathname.split('/').filter(Boolean).pop(); - - const { blob, filename } = await waha.downloadMedia(reqID); - - const objectUrl = URL.createObjectURL(blob); - e.target.href = objectUrl; - - if (blob.type.startsWith('image/')) { - a.textContent = ""; - const img = document.createElement('img'); - img.classList.add('message-image-attachement'); - img.src = objectUrl; - a.appendChild(img); - } else { - e.target.textContent = filename || `Download ${mediaMsg.media.filename}`; + const clickListener = async (e) => { + a.removeEventListener('click', clickListener); + a.innerText = `[Downloading]`; + const mediaMsg = msg.media ? msg : await waha.getSingleChatMessage(chatId, msg.id, true); + console.log(mediaMsg.media.url); + const url = new URL(mediaMsg.media.url); + const reqID = url.pathname.split('/').filter(Boolean).pop(); + + const { blob, filename } = await waha.downloadMedia(reqID); + + const objectUrl = URL.createObjectURL(blob); + e.target.href = objectUrl; + + if (blob.type.startsWith('image/')) { + a.textContent = ""; + const img = document.createElement('img'); + img.classList.add('message-image-attachement'); + img.src = objectUrl; + a.appendChild(img); + } else { + e.target.textContent = filename || `Download ${mediaMsg.media.filename}`; + } } + + a.addEventListener('click', clickListener); } contentEl.appendChild(a); - a.addEventListener('click', clickListener); - if (msg._data.mimetype.startsWith('image/')) { + if (!isLocal && msg._data?.mimetype?.startsWith('image/')) { a.click(); } } - + const meta = document.createElement('div'); meta.className = 'message-meta'; @@ -233,7 +258,7 @@ export const ui = { let uid = ""; function getPrevMessageElem() { - return elements.messagesContainer.lastElementChild; // <‑‑ key change + return elements.messagesContainer.lastElementChild; } const prevMsgEl = getPrevMessageElem(); @@ -266,6 +291,21 @@ export const ui = { } }, + updateMessageTick(id, status) { + let statusCheck; + if (status === 'read') { + statusCheck = ''; + } else if (status === 'delivered') { + statusCheck = ''; + } else if (status === 'sending') { + statusCheck = ''; + } else { + statusCheck = ''; + } + + document.getElementById(id).querySelector('.message-meta').outerHTML = statusCheck; + }, + toggleChatBottomBar() { elements.chatBottomBar.classList.toggle("collapsed"); }