Fix duplicate messages arriving through websocket
This commit is contained in:
parent
f6310d3065
commit
dc5db6aac6
5 changed files with 24 additions and 24 deletions
15
js/app.js
15
js/app.js
|
|
@ -2,7 +2,7 @@ import { config } from "./config.js";
|
||||||
import { waha } from "./waha.js";
|
import { waha } from "./waha.js";
|
||||||
import { ui, elements } from "./ui.js";
|
import { ui, elements } from "./ui.js";
|
||||||
import { websocket } from "./websocket.js";
|
import { websocket } from "./websocket.js";
|
||||||
import { compensateMessageOrdering, formatTime } from "./utils.js";
|
import { compensateMessageOrdering, formatTime, normalizeId } from "./utils.js";
|
||||||
import { fetchChats, getAppUser, getChatMessages, getChats, updateOnlineStatus } from "./storage.js";
|
import { fetchChats, getAppUser, getChatMessages, getChats, updateOnlineStatus } from "./storage.js";
|
||||||
import { upsertMessages } from "./db.js";
|
import { upsertMessages } from "./db.js";
|
||||||
|
|
||||||
|
|
@ -114,13 +114,6 @@ function initWebSocket() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeChatId(raw) {
|
|
||||||
if (!raw) return null;
|
|
||||||
if (typeof raw === 'object') {
|
|
||||||
return raw._serialized || raw.user || JSON.stringify(raw);
|
|
||||||
}
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleIncomingMessage(msg) {
|
async function handleIncomingMessage(msg) {
|
||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
|
|
@ -130,7 +123,7 @@ async function handleIncomingMessage(msg) {
|
||||||
ui.updateChatInChatList(msg);
|
ui.updateChatInChatList(msg);
|
||||||
|
|
||||||
const rawChatId = msg.chatId || msg.from || (msg.chat && msg.chat.id);
|
const rawChatId = msg.chatId || msg.from || (msg.chat && msg.chat.id);
|
||||||
const msgChatId = normalizeChatId(rawChatId);
|
const msgChatId = normalizeId(rawChatId);
|
||||||
if (!msgChatId) {
|
if (!msgChatId) {
|
||||||
console.warn('[WS] Could not resolve chatId from payload:', msg);
|
console.warn('[WS] Could not resolve chatId from payload:', msg);
|
||||||
return;
|
return;
|
||||||
|
|
@ -142,7 +135,7 @@ async function handleIncomingMessage(msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeChatState && activeChatState.id === msgChatId) {
|
if (activeChatState && activeChatState.id === msgChatId) {
|
||||||
const msgId = normalizeChatId(msg.id) || msg.id;
|
const msgId = normalizeId(msg.id) || msg.id;
|
||||||
const exists = document.getElementById(msgId);
|
const exists = document.getElementById(msgId);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
ui.appendSingleMessage({ ...msg, chatId: msgChatId }, activeChatState.name, (await getAppUser()).id);
|
ui.appendSingleMessage({ ...msg, chatId: msgChatId }, activeChatState.name, (await getAppUser()).id);
|
||||||
|
|
@ -246,7 +239,7 @@ async function sendMessage() {
|
||||||
const tempBubble = document.getElementById(tempMsg.id);
|
const tempBubble = document.getElementById(tempMsg.id);
|
||||||
if (tempBubble) {
|
if (tempBubble) {
|
||||||
if (responseData && responseData.id) {
|
if (responseData && responseData.id) {
|
||||||
tempBubble.id = responseData.id;
|
tempBubble.id = normalizeId(responseData.id);
|
||||||
}
|
}
|
||||||
const meta = tempBubble.querySelector('.message-meta');
|
const meta = tempBubble.querySelector('.message-meta');
|
||||||
meta.innerHTML = `<span>${formatTime(new Date())}</span><span style="width:14px; height:14px;" class="mif-done">`;
|
meta.innerHTML = `<span>${formatTime(new Date())}</span><span style="width:14px; height:14px;" class="mif-done">`;
|
||||||
|
|
|
||||||
12
js/db.js
12
js/db.js
|
|
@ -1,15 +1,9 @@
|
||||||
|
import { normalizeId } from "./utils.js";
|
||||||
|
|
||||||
const DB_NAME = "pandora";
|
const DB_NAME = "pandora";
|
||||||
const DB_VERSION = 6;
|
const DB_VERSION = 6;
|
||||||
let dbPromise = null;
|
let dbPromise = null;
|
||||||
|
|
||||||
function normalizeId(raw) {
|
|
||||||
if (!raw) return null;
|
|
||||||
if (typeof raw === 'object') {
|
|
||||||
return raw._serialized || raw.user || JSON.stringify(raw);
|
|
||||||
}
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDb() {
|
function openDb() {
|
||||||
if (dbPromise) return dbPromise;
|
if (dbPromise) return dbPromise;
|
||||||
|
|
||||||
|
|
@ -120,7 +114,7 @@ function mapMessage(m) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_data: m._data,
|
_data: m._data,
|
||||||
id: m.id,
|
id: normalizeId(m.id),
|
||||||
timestamp: m.timestamp,
|
timestamp: m.timestamp,
|
||||||
body: m.body,
|
body: m.body,
|
||||||
from: from,
|
from: from,
|
||||||
|
|
|
||||||
6
js/ui.js
6
js/ui.js
|
|
@ -1,4 +1,4 @@
|
||||||
import { formatTime } from "./utils.js";
|
import { formatTime, normalizeId } from "./utils.js";
|
||||||
import { waha } from "./waha.js";
|
import { waha } from "./waha.js";
|
||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
import { getChatPicture, getMessage, getMedia, getMoreChatMessages } from "./storage.js";
|
import { getChatPicture, getMessage, getMedia, getMoreChatMessages } from "./storage.js";
|
||||||
|
|
@ -209,7 +209,7 @@ export const ui = {
|
||||||
|
|
||||||
const groupDiv = document.createElement('div');
|
const groupDiv = document.createElement('div');
|
||||||
groupDiv.className = `message-group ${isOutgoing ? 'outgoing' : 'incoming'}`;
|
groupDiv.className = `message-group ${isOutgoing ? 'outgoing' : 'incoming'}`;
|
||||||
groupDiv.id = msg.id;
|
groupDiv.id = normalizeId(msg._serialized ? msg : msg.id);
|
||||||
groupDiv.dataset.timestamp = msg.timestamp;
|
groupDiv.dataset.timestamp = msg.timestamp;
|
||||||
groupDiv.dataset.from = msg.participant || msg.from;
|
groupDiv.dataset.from = msg.participant || msg.from;
|
||||||
|
|
||||||
|
|
@ -267,7 +267,7 @@ export const ui = {
|
||||||
const clickListener = async (e) => {
|
const clickListener = async (e) => {
|
||||||
a.removeEventListener('click', clickListener);
|
a.removeEventListener('click', clickListener);
|
||||||
a.innerText = `[Downloading]`;
|
a.innerText = `[Downloading]`;
|
||||||
const mediaMsg = msg.media ? msg : await getMessage(chatId, msg.id, true);
|
const mediaMsg = msg.media ? msg : await getMessage(chatId, normalizeId(msg._serialized ? msg : msg.id), true);
|
||||||
if (!mediaMsg || !mediaMsg?.media.url) {
|
if (!mediaMsg || !mediaMsg?.media.url) {
|
||||||
a.addEventListener('click', clickListener);
|
a.addEventListener('click', clickListener);
|
||||||
a.innerText = `[Error, click to try again]`
|
a.innerText = `[Error, click to try again]`
|
||||||
|
|
|
||||||
13
js/utils.js
13
js/utils.js
|
|
@ -100,3 +100,16 @@ export function getBase64(file) {
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a WhatsApp ID (chatId, messageId, etc.) to its string representation
|
||||||
|
* @param {string|object} raw
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
export function normalizeId(raw) {
|
||||||
|
if (!raw) return null;
|
||||||
|
if (typeof raw === 'object') {
|
||||||
|
return raw._serialized || raw.user || JSON.stringify(raw);
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ export const websocket = {
|
||||||
|
|
||||||
const apiKey = config.apiKey;
|
const apiKey = config.apiKey;
|
||||||
const session = config.session;
|
const session = config.session;
|
||||||
const events = ['session.status', 'message', 'message.any'];
|
const events = ['session.status', 'message.any'];
|
||||||
|
|
||||||
const queryParams = new URLSearchParams();
|
const queryParams = new URLSearchParams();
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue