Ported to Cordova, it sucks though, I'm trying Capacitor tomorrow.
27
.gitignore
vendored
|
|
@ -1,2 +1,25 @@
|
|||
.env
|
||||
node_modules
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Generated by package manager
|
||||
node_modules/
|
||||
|
||||
# Generated by Cordova
|
||||
/plugins/
|
||||
/platforms/
|
||||
|
|
|
|||
BIN
android/app/src/main/res/drawable-land-hdpi/splash.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
android/app/src/main/res/drawable-land-mdpi/splash.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/drawable-land-xhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
android/app/src/main/res/drawable-land-xxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
android/app/src/main/res/drawable-land-xxxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
android/app/src/main/res/drawable-port-hdpi/splash.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
android/app/src/main/res/drawable-port-mdpi/splash.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/drawable-port-xhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
android/app/src/main/res/drawable-port-xxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
android/app/src/main/res/drawable-port-xxxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
android/app/src/main/res/drawable/splash.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 864 B |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 983 B |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 471 B |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 983 B |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
45
config.xml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="org.adrianvictor.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>Pandora</name>
|
||||
<description>
|
||||
A messaging client for WAHA API.
|
||||
</description>
|
||||
<author email="adrianvictor@disroot.org" href="https://adrianvic.github.io">
|
||||
Adrian Victor (tenkuma)
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<platform name="android">
|
||||
<adaptive-icon background="#000000" foreground="resources/android/icon.png" />
|
||||
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
|
||||
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
|
||||
<icon background="resources/android/icon/ldpi-background.png" density="ldpi" foreground="resources/android/icon/ldpi-foreground.png" />
|
||||
<icon background="resources/android/icon/mdpi-background.png" density="mdpi" foreground="resources/android/icon/mdpi-foreground.png" />
|
||||
<icon background="resources/android/icon/hdpi-background.png" density="hdpi" foreground="resources/android/icon/hdpi-foreground.png" />
|
||||
<icon background="resources/android/icon/xhdpi-background.png" density="xhdpi" foreground="resources/android/icon/xhdpi-foreground.png" />
|
||||
<icon background="resources/android/icon/xxhdpi-background.png" density="xxhdpi" foreground="resources/android/icon/xxhdpi-foreground.png" />
|
||||
<icon background="resources/android/icon/xxxhdpi-background.png" density="xxxhdpi" foreground="resources/android/icon/xxxhdpi-foreground.png" />
|
||||
<preference name="AndroidWindowSplashScreenAnimatedIcon" value="resources/icon.png" />
|
||||
<preference name="AndroidWindowSplashScreenBackground" value="#000000" />
|
||||
<preference name="AndroidWindowSplashScreenAnimationDuration" value="200" />
|
||||
<preference name="AndroidWindowSplashScreenAnimatedIcon" value="resources/android/splash/icon.png" />
|
||||
<preference name="SplashScreenBackgroundColor" value="#000000" />
|
||||
</platform>
|
||||
</widget>
|
||||
21
index.js
|
|
@ -1,21 +0,0 @@
|
|||
import express from "express";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3200;
|
||||
|
||||
// Resolve __dirname in ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Serve static assets from the public folder
|
||||
app.use(express.static(path.join(__dirname, "docs")));
|
||||
|
||||
// Start static file server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`==================================================`);
|
||||
console.log(` Pandora Chat Static Server is running!`);
|
||||
console.log(` URL: http://localhost:${PORT}`);
|
||||
console.log(`==================================================`);
|
||||
});
|
||||
1960
package-lock.json
generated
31
package.json
|
|
@ -1,19 +1,26 @@
|
|||
{
|
||||
"name": "pandora",
|
||||
"name": "org.adrianvictor.pandorabox",
|
||||
"displayName": "Pandora",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "A messaging client for WAHA API.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"runAndroid": "cordova run android --device",
|
||||
"runBrowser": "cordova run browser"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^1.18.1",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1"
|
||||
"keywords": [
|
||||
"ecosystem:cordova"
|
||||
],
|
||||
"author": "Apache Cordova Team",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"cordova-android": "github:apache/cordova-android#14.0.1",
|
||||
"cordova-browser": "^7.0.0"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"browser",
|
||||
"android"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
resources/android/icon-background.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
resources/android/icon-foreground.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
resources/android/icon.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
resources/android/icon/drawable-hdpi-icon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/android/icon/drawable-ldpi-icon.png
Normal file
|
After Width: | Height: | Size: 706 B |
BIN
resources/android/icon/drawable-mdpi-icon.png
Normal file
|
After Width: | Height: | Size: 983 B |
BIN
resources/android/icon/drawable-xhdpi-icon.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
resources/android/icon/drawable-xxhdpi-icon.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/android/icon/drawable-xxxhdpi-icon.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
resources/android/icon/hdpi-background.png
Normal file
|
After Width: | Height: | Size: 864 B |
BIN
resources/android/icon/hdpi-foreground.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
resources/android/icon/ldpi-background.png
Normal file
|
After Width: | Height: | Size: 353 B |
BIN
resources/android/icon/ldpi-foreground.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/android/icon/mdpi-background.png
Normal file
|
After Width: | Height: | Size: 471 B |
BIN
resources/android/icon/mdpi-foreground.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
resources/android/icon/xhdpi-background.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/android/icon/xhdpi-foreground.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
BIN
resources/android/icon/xxhdpi-background.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
resources/android/icon/xxhdpi-foreground.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
resources/android/icon/xxxhdpi-background.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
resources/android/icon/xxxhdpi-foreground.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
resources/android/splash.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
resources/android/splash/drawable-land-hdpi-screen.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
resources/android/splash/drawable-land-ldpi-screen.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
resources/android/splash/drawable-land-mdpi-screen.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
resources/android/splash/drawable-land-xhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
resources/android/splash/drawable-land-xxhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
resources/android/splash/drawable-land-xxxhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
resources/android/splash/drawable-port-hdpi-screen.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
resources/android/splash/drawable-port-ldpi-screen.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
resources/android/splash/drawable-port-mdpi-screen.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
resources/android/splash/drawable-port-xhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
resources/android/splash/drawable-port-xxhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
resources/android/splash/drawable-port-xxxhdpi-screen.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
|
|
@ -1,77 +0,0 @@
|
|||
import axios from "axios";
|
||||
|
||||
const session = "session_01kxc62bk5fs8vh4v127k88a7j";
|
||||
const wahaBaseUrl = "http://inspiran.beetal-castor.ts.net:3100";
|
||||
const apiKey = process.env.WAHA_API_KEY;
|
||||
|
||||
const client = axios.create({
|
||||
baseURL: wahaBaseUrl,
|
||||
headers: {
|
||||
"X-API-KEY": apiKey,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
export const wahaService = {
|
||||
getConfigStatus() {
|
||||
return {
|
||||
wahaBaseUrl,
|
||||
session,
|
||||
hasApiKey: !!apiKey
|
||||
};
|
||||
},
|
||||
|
||||
async getChats() {
|
||||
const response = await client.get(`/api/${session}/chats`, { timeout: 5000 });
|
||||
|
||||
if (!Array.isArray(response.data)) {
|
||||
throw new Error("Invalid chats response format from WAHA API");
|
||||
}
|
||||
|
||||
return response.data.map(chat => {
|
||||
let chatId = chat.id;
|
||||
if (chatId && typeof chatId === "object") {
|
||||
chatId = chatId._serialized || chatId.user || JSON.stringify(chatId);
|
||||
}
|
||||
return {
|
||||
id: chatId || chat.chatId || chat.name,
|
||||
name: chat.name || "Unknown Contact",
|
||||
unreadCount: chat.unreadCount || 0,
|
||||
lastMessage: chat.lastMessage?.body || chat.lastMessageText || "Click to open chat",
|
||||
timestamp: chat.lastMessage?.timestamp || new Date()
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
async getChatMessages(chatId) {
|
||||
const response = await client.get(`/api/${session}/chats/${chatId}/messages?downloadMedia=false&merge=true&limit=10`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async getMyInfo(chatId) {
|
||||
const response = await client.get(`/api/${session}/me`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async startTyping(chatId) {
|
||||
await client.post("/api/startTyping", {
|
||||
chatId,
|
||||
session
|
||||
}, { timeout: 3000 });
|
||||
},
|
||||
|
||||
async stopTyping(chatId) {
|
||||
await client.post("/api/stopTyping", {
|
||||
chatId,
|
||||
session
|
||||
}, { timeout: 2000 });
|
||||
},
|
||||
|
||||
async sendTextMessage(chatId, text) {
|
||||
const response = await client.post("/api/sendText", {
|
||||
chatId,
|
||||
text
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
|
@ -10,11 +10,26 @@ let userInfo;
|
|||
|
||||
// Initialize app when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
userInfo = await waha.getMyInfo();
|
||||
setupEventListeners();
|
||||
try {
|
||||
userInfo = await waha.getMyInfo();
|
||||
fetchChats();
|
||||
checkWahaStatus();
|
||||
initWebSocket();
|
||||
} catch (error) {
|
||||
console.error('Failed to load chats:', error);
|
||||
elements.chatList.innerHTML = `
|
||||
<li class="loading-chats" style="color: var(--text-primary); text-align: center; padding: 20px;">
|
||||
<p>Connection to WAHA failed.</p>
|
||||
<p style="font-size: 0.75rem; color: var(--text-primary); margin-top: 8px;">
|
||||
Ensure WAHA server is running and CORS is enabled, or click Settings to configure.
|
||||
</p>
|
||||
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 8px;">${error.message}</p>
|
||||
</li>
|
||||
`;
|
||||
} finally {
|
||||
elements.chatsLoader.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
function setupEventListeners() {
|
||||
|
|
@ -155,7 +170,18 @@ async function selectChat(chat) {
|
|||
elements.activeChatName.textContent = chat.name.toUpperCase();
|
||||
elements.activeChatAvatar.textContent = chat.name ? chat.name.substring(0, 1).toUpperCase() : '?';
|
||||
|
||||
elements.messagesContainer.innerHTML = '<div class="loading-chats"><div class="spinner"></div><span>Loading messages...</span></div>';
|
||||
elements.messagesContainer.innerHTML = `
|
||||
<div class="loading-chats">
|
||||
<div class='dots'>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<span>Loading messages...
|
||||
</span>
|
||||
</div>`;
|
||||
|
||||
if (window.innerWidth <= 768) {
|
||||
elements.sidebar.classList.add('hidden');
|
||||
|
|
@ -164,7 +190,7 @@ async function selectChat(chat) {
|
|||
try {
|
||||
const rawMessages = await waha.getChatMessages(chat.id);
|
||||
const processedMessages = compensateMessageOrdering(rawMessages);
|
||||
ui.renderMessages(processedMessages, chat.name, userInfo.id);
|
||||
ui.renderMessages(processedMessages, chat.name, userInfo.id, chat.id);
|
||||
} catch (error) {
|
||||
console.error('Failed to load messages:', error);
|
||||
elements.messagesContainer.innerHTML = '<div class="loading-chats">Error loading messages</div>';
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
document.querySelectorAll(".dots").forEach(el => {
|
||||
el.classList.add("animate");
|
||||
});
|
||||
}, 1000);
|
||||
}, 100);
|
||||
});
|
||||
</script>
|
||||
<div class="app-container">
|
||||
|
|
@ -77,29 +77,29 @@ export const ui = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render sidebar contact chats list
|
||||
*/
|
||||
renderChatList(chats, activeChat, onChatSelect) {
|
||||
async renderChatList(chats, activeChat, onChatSelect) {
|
||||
elements.chatList.innerHTML = '';
|
||||
|
||||
chats.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
if (chats.length === 0) {
|
||||
elements.chatList.innerHTML = `<li class="loading-chats">No chats found</li>`;
|
||||
return;
|
||||
}
|
||||
|
||||
chats.forEach(async (chat) => {
|
||||
for (const chat of chats) {
|
||||
console.log(chat);
|
||||
const li = document.createElement('li');
|
||||
li.className = `chat-item ${activeChat && activeChat.id === chat.id ? 'active' : ''}`;
|
||||
li.dataset.id = chat.id;
|
||||
const picture = await waha.getChatPicture(chat.id);
|
||||
|
||||
const picture = await waha.getChatPicture(chat.id);
|
||||
const initials = chat.name ? chat.name.substring(0, 1).toUpperCase() : '?';
|
||||
const hasUnread = chat.unreadCount && chat.unreadCount > 0;
|
||||
const timeStr = formatTime(chat.timestamp || new Date());
|
||||
|
||||
li.innerHTML = `
|
||||
<div class="avatar"><img src="${picture.url}"></div>
|
||||
<div class="avatar"><img src="${picture.url}" alt="${initials}"></div>
|
||||
<div class="chat-item-info">
|
||||
<div class="chat-item-meta">
|
||||
<span class="chat-item-name">${chat.name}</span>
|
||||
|
|
@ -114,13 +114,13 @@ export const ui = {
|
|||
|
||||
li.addEventListener('click', () => onChatSelect(chat));
|
||||
elements.chatList.appendChild(li);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render chat message log inside chat view container
|
||||
*/
|
||||
async renderMessages(messages, activeChatName, userID) {
|
||||
async renderMessages(messages, activeChatName, userID, chatId) {
|
||||
elements.messagesContainer.innerHTML = '';
|
||||
|
||||
if (messages.length === 0) {
|
||||
|
|
@ -129,7 +129,7 @@ export const ui = {
|
|||
}
|
||||
|
||||
for (const msg of messages) {
|
||||
this.appendSingleMessage(msg, activeChatName, userID);
|
||||
this.appendSingleMessage(msg, activeChatName, userID, chatId);
|
||||
}
|
||||
|
||||
lucide.createIcons();
|
||||
|
|
@ -139,7 +139,7 @@ export const ui = {
|
|||
/**
|
||||
* Append a single message (used for optimistic updates immediately upon sending)
|
||||
*/
|
||||
appendSingleMessage(msg, activeChatName, userID) {
|
||||
appendSingleMessage(msg, activeChatName, userID, chatId) {
|
||||
console.log(msg);
|
||||
const isOutgoing = msg.fromMe || msg.sender === 'me';
|
||||
|
||||
|
|
@ -179,12 +179,14 @@ export const ui = {
|
|||
contentEl.appendChild(textEl);
|
||||
bubble.appendChild(contentEl);
|
||||
|
||||
if (msg.media?.url) {
|
||||
if (msg.hasMedia) {
|
||||
const a = document.createElement('a');
|
||||
a.innerText = `Request media (${msg.media.filename})`;
|
||||
a.innerText = `[Request media]`;
|
||||
|
||||
a.addEventListener('click', async (e) => {
|
||||
const url = new URL(msg.media.url);
|
||||
const mediaMsg = await waha.getSingleChatMessage(chatId, msg.id, true);
|
||||
|
||||
const url = new URL(mediaMsg.media.url);
|
||||
const reqID = url.pathname.split('/').filter(Boolean).pop();
|
||||
|
||||
const { blob, filename } = await waha.downloadMedia(reqID);
|
||||
|
|
@ -199,7 +201,7 @@ export const ui = {
|
|||
img.src = objectUrl;
|
||||
a.appendChild(img);
|
||||
} else {
|
||||
e.target.textContent = filename || `Download ${msg.media.filename}`;
|
||||
e.target.textContent = filename || `Download ${mediaMsg.media.filename}`;
|
||||
}
|
||||
|
||||
})
|
||||
|
|
@ -89,7 +89,11 @@ export const waha = {
|
|||
},
|
||||
|
||||
async getChatMessages(chatId) {
|
||||
return request(`/api/${config.session}/chats/${chatId}/messages?downloadMedia=true&limit=40`);
|
||||
return request(`/api/${config.session}/chats/${chatId}/messages?downloadMedia=false&limit=40`);
|
||||
},
|
||||
|
||||
async getSingleChatMessage(chatId, messageId, downladMedia) {
|
||||
return request(`/api/${config.session}/chats/${chatId}/messages/${messageId}?downloadMedia=${downladMedia}`);
|
||||
},
|
||||
|
||||
async getChatPicture(chatId) {
|
||||