From f804ffed9338bf645da1da1a67ad7ef06ff308a7 Mon Sep 17 00:00:00 2001 From: Tom Deckers Date: Tue, 1 Sep 2026 15:15:35 +0200 Subject: [PATCH 1/3] fix: support LID direct chats --- .vscode/extensions.json | 3 + .vscode/settings.json | 6 ++ package.json | 3 +- src/lib/chat/identity.ts | 64 +++++++++++++++ src/lib/queries/chat/findMessages.ts | 25 ++++-- src/pages/instance/Chat/index.tsx | 3 +- src/pages/instance/Chat/messages.tsx | 3 +- src/types/evolution.types.ts | 1 + tests/chat-identity.test.ts | 116 +++++++++++++++++++++++++++ tests/tsconfig.json | 15 ++++ 10 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 src/lib/chat/identity.ts create mode 100644 tests/chat-identity.test.ts create mode 100644 tests/tsconfig.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1d7ac85 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fb7657b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } +} diff --git a/package.json b/package.json index d655357..ec94ef0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "lint:check": "eslint ./src/**/*.{ts,tsx} --ext .ts,.tsx", "type-check": "tsc --noEmit", "preview": "vite preview", - "test": "echo \"No tests specified\" && exit 0", + "test": "npm run test:identity", + "test:identity": "tsc -p tests/tsconfig.json && node --test /tmp/evolution-manager-v2-identity-test/tests/chat-identity.test.js", "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"" }, diff --git a/src/lib/chat/identity.ts b/src/lib/chat/identity.ts new file mode 100644 index 0000000..3827dcf --- /dev/null +++ b/src/lib/chat/identity.ts @@ -0,0 +1,64 @@ +type ConversationKey = { + id?: string; + remoteJid?: string; + remoteJidAlt?: string; +}; + +type MessageRecord = { + key?: ConversationKey; +}; + +export function isDirectChatJid(jid: string): boolean { + return jid.endsWith("@s.whatsapp.net") || jid.endsWith("@lid"); +} + +export function messageMatchesConversation(key: ConversationKey | null | undefined, selectedJid: string): boolean { + if (!key) { + return false; + } + + return key.remoteJid === selectedJid || key.remoteJidAlt === selectedJid; +} + +export function mergeMessagesByKeyId(groups: T[][]): T[] { + const seenIds = new Set(); + const merged: T[] = []; + + for (const group of groups) { + for (const record of group) { + const id = record.key?.id; + if (id) { + if (seenIds.has(id)) { + continue; + } + seenIds.add(id); + merged.push(record); + continue; + } + + merged.push(record); + } + } + + return merged; +} + +export function messageRecordsFromResponse(payload: unknown): MessageRecord[] { + if (Array.isArray(payload)) { + return payload; + } + + if ( + payload && + typeof payload === "object" && + "messages" in payload && + payload.messages && + typeof payload.messages === "object" && + "records" in payload.messages && + Array.isArray(payload.messages.records) + ) { + return payload.messages.records; + } + + return []; +} diff --git a/src/lib/queries/chat/findMessages.ts b/src/lib/queries/chat/findMessages.ts index 292742e..e2402bf 100644 --- a/src/lib/queries/chat/findMessages.ts +++ b/src/lib/queries/chat/findMessages.ts @@ -1,9 +1,14 @@ import { useQuery } from "@tanstack/react-query"; +import { mergeMessagesByKeyId, messageRecordsFromResponse } from "@/lib/chat/identity"; +import { Message } from "@/types/evolution.types"; + import { api } from "../api"; import { UseQueryParams } from "../types"; import { FindMessagesResponse } from "./types"; +export { messageRecordsFromResponse }; + interface IParams { instanceName: string; remoteJid: string; @@ -11,14 +16,22 @@ interface IParams { const queryKey = (params: Partial) => ["chats", "findMessages", JSON.stringify(params)]; -export const findMessages = async ({ instanceName, remoteJid }: IParams) => { - const response = await api.post(`/chat/findMessages/${instanceName}`, { +export const findMessages = async ({ instanceName, remoteJid }: IParams): Promise => { + const url = `/chat/findMessages/${instanceName}`; + const primaryRequest = api.post(url, { where: { key: { remoteJid } }, }); - if (response.data?.messages?.records) { - return response.data.messages.records; - } - return response.data; + const alternateRequest = api + .post(url, { + where: { key: { remoteJidAlt: remoteJid } }, + }) + .then((response) => messageRecordsFromResponse(response.data) as Message[]) + .catch(() => [] as Message[]); + + const [primaryResponse, alternateRecords] = await Promise.all([primaryRequest, alternateRequest]); + const primaryRecords = messageRecordsFromResponse(primaryResponse.data) as Message[]; + + return mergeMessagesByKeyId([primaryRecords, alternateRecords]); }; export const useFindMessages = (props: UseQueryParams & Partial) => { diff --git a/src/pages/instance/Chat/index.tsx b/src/pages/instance/Chat/index.tsx index 07ff877..7ad2068 100644 --- a/src/pages/instance/Chat/index.tsx +++ b/src/pages/instance/Chat/index.tsx @@ -9,6 +9,7 @@ import { useNavigate, useParams } from "react-router-dom"; import { useInstance } from "@/contexts/InstanceContext"; +import { isDirectChatJid } from "@/lib/chat/identity"; import { useFindChats } from "@/lib/queries/chat/findChats"; import { getToken, TOKEN_ID } from "@/lib/queries/token"; import { cn } from "@/lib/utils"; @@ -115,7 +116,7 @@ function Chat() { const visibleChats = useMemo(() => { const isContacts = kind === "contacts"; const filtered = allChats.filter((c) => - isContacts ? c.remoteJid.includes("@s.whatsapp.net") : c.remoteJid.includes("@g.us"), + isContacts ? isDirectChatJid(c.remoteJid) : c.remoteJid.includes("@g.us"), ); if (!search.trim()) return filtered; const q = search.toLowerCase(); diff --git a/src/pages/instance/Chat/messages.tsx b/src/pages/instance/Chat/messages.tsx index 6331202..a27291f 100644 --- a/src/pages/instance/Chat/messages.tsx +++ b/src/pages/instance/Chat/messages.tsx @@ -9,6 +9,7 @@ import { Textarea } from "@/components/ui/textarea"; import { useInstance } from "@/contexts/InstanceContext"; +import { messageMatchesConversation } from "@/lib/chat/identity"; import { useFindChat } from "@/lib/queries/chat/findChat"; import { useFindMessages } from "@/lib/queries/chat/findMessages"; import { useSendMessage, useSendMedia } from "@/lib/queries/chat/sendMessage"; @@ -460,7 +461,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa return; } - if (data?.data?.key?.remoteJid !== remoteJid) { + if (!messageMatchesConversation(data?.data?.key, remoteJid)) { return; } diff --git a/src/types/evolution.types.ts b/src/types/evolution.types.ts index 8de75ff..1e5737a 100644 --- a/src/types/evolution.types.ts +++ b/src/types/evolution.types.ts @@ -68,6 +68,7 @@ export type Key = { id: string; fromMe: boolean; remoteJid: string; + remoteJidAlt?: string; participant?: string; }; diff --git a/tests/chat-identity.test.ts b/tests/chat-identity.test.ts new file mode 100644 index 0000000..2dce8b7 --- /dev/null +++ b/tests/chat-identity.test.ts @@ -0,0 +1,116 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +import { isDirectChatJid, mergeMessagesByKeyId, messageMatchesConversation, messageRecordsFromResponse } from "../src/lib/chat/identity"; + +describe("isDirectChatJid", () => { + it("detects phone direct chats", () => { + assert.equal(isDirectChatJid("5511999999999@s.whatsapp.net"), true); + }); + + it("detects LID direct chats", () => { + assert.equal(isDirectChatJid("123456789012345@lid"), true); + }); + + it("rejects groups", () => { + assert.equal(isDirectChatJid("120363012345678901@g.us"), false); + }); + + it("rejects broadcasts", () => { + assert.equal(isDirectChatJid("status@broadcast"), false); + }); +}); + +describe("messageMatchesConversation", () => { + it("matches a selected phone JID through a LID-primary key via remoteJidAlt", () => { + assert.equal( + messageMatchesConversation( + { + remoteJid: "123456789012345@lid", + remoteJidAlt: "5511999999999@s.whatsapp.net", + }, + "5511999999999@s.whatsapp.net", + ), + true, + ); + }); + + it("matches a selected LID JID through a phone-primary key via remoteJidAlt", () => { + assert.equal( + messageMatchesConversation( + { + remoteJid: "5511999999999@s.whatsapp.net", + remoteJidAlt: "123456789012345@lid", + }, + "123456789012345@lid", + ), + true, + ); + }); +}); + +describe("mergeMessagesByKeyId", () => { + it("keeps a phone-primary and LID-primary pair when they have different IDs", () => { + const phonePrimary = { + key: { + id: "phone-msg-1", + remoteJid: "5511999999999@s.whatsapp.net", + remoteJidAlt: "123456789012345@lid", + }, + }; + const lidPrimary = { + key: { + id: "lid-msg-1", + remoteJid: "123456789012345@lid", + remoteJidAlt: "5511999999999@s.whatsapp.net", + }, + }; + + assert.deepEqual(mergeMessagesByKeyId([[phonePrimary], [lidPrimary]]), [phonePrimary, lidPrimary]); + }); + + it("de-duplicates records that share the same nonempty key id, keeping the first", () => { + const first = { + key: { + id: "shared-id", + remoteJid: "5511999999999@s.whatsapp.net", + }, + source: "primary", + }; + const duplicate = { + key: { + id: "shared-id", + remoteJid: "123456789012345@lid", + }, + source: "alternate", + }; + + assert.deepEqual(mergeMessagesByKeyId([[first], [duplicate]]), [first]); + }); + + it("keeps unkeyed records without replacing keyed ones", () => { + const keyed = { key: { id: "keyed-1", remoteJid: "5511999999999@s.whatsapp.net" } }; + const unkeyed = { key: { remoteJid: "5511999999999@s.whatsapp.net" } }; + + assert.deepEqual(mergeMessagesByKeyId([[keyed], [unkeyed]]), [keyed, unkeyed]); + }); +}); + +describe("messageRecordsFromResponse", () => { + it("normalizes a raw Message array", () => { + const records = [{ key: { id: "raw-1", remoteJid: "5511999999999@s.whatsapp.net" } }]; + assert.equal(messageRecordsFromResponse(records), records); + }); + + it("normalizes { messages: { records } } payloads", () => { + const records = [{ key: { id: "nested-1", remoteJid: "123456789012345@lid" } }]; + assert.deepEqual(messageRecordsFromResponse({ messages: { records } }), records); + }); + + it("returns an empty array for unrecognized shapes", () => { + assert.deepEqual(messageRecordsFromResponse(undefined), []); + assert.deepEqual(messageRecordsFromResponse(null), []); + assert.deepEqual(messageRecordsFromResponse({}), []); + assert.deepEqual(messageRecordsFromResponse({ messages: {} }), []); + }); +}); diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..51886df --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "types": ["node"], + "rootDir": "..", + "outDir": "/tmp/evolution-manager-v2-identity-test", + "noEmitOnError": true + }, + "files": ["../src/lib/chat/identity.ts", "./chat-identity.test.ts"] +} From 9dc5e650da512e86b43d88ecf7db43afabeacc0d Mon Sep 17 00:00:00 2001 From: Tom Deckers Date: Tue, 1 Sep 2026 15:23:59 +0200 Subject: [PATCH 2/3] fix: harden LID history lookup --- .gitignore | 3 ++- package.json | 2 +- src/lib/queries/chat/findMessages.ts | 16 +++++++++------- tests/chat-identity.test.ts | 15 ++++++++++++++- tests/tsconfig.json | 6 +++--- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index abef4d4..74ae217 100644 --- a/.gitignore +++ b/.gitignore @@ -154,4 +154,5 @@ build/ # Temporary files tmp/ -temp/ \ No newline at end of file +temp/ +tests/.tmp/ diff --git a/package.json b/package.json index ec94ef0..36715fc 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "type-check": "tsc --noEmit", "preview": "vite preview", "test": "npm run test:identity", - "test:identity": "tsc -p tests/tsconfig.json && node --test /tmp/evolution-manager-v2-identity-test/tests/chat-identity.test.js", + "test:identity": "tsc -p tests/tsconfig.json && node --test tests/.tmp/identity-test/tests/chat-identity.test.js", "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"" }, diff --git a/src/lib/queries/chat/findMessages.ts b/src/lib/queries/chat/findMessages.ts index e2402bf..e673da3 100644 --- a/src/lib/queries/chat/findMessages.ts +++ b/src/lib/queries/chat/findMessages.ts @@ -1,6 +1,6 @@ import { useQuery } from "@tanstack/react-query"; -import { mergeMessagesByKeyId, messageRecordsFromResponse } from "@/lib/chat/identity"; +import { isDirectChatJid, mergeMessagesByKeyId, messageMatchesConversation, messageRecordsFromResponse } from "@/lib/chat/identity"; import { Message } from "@/types/evolution.types"; import { api } from "../api"; @@ -21,12 +21,14 @@ export const findMessages = async ({ instanceName, remoteJid }: IParams): Promis const primaryRequest = api.post(url, { where: { key: { remoteJid } }, }); - const alternateRequest = api - .post(url, { - where: { key: { remoteJidAlt: remoteJid } }, - }) - .then((response) => messageRecordsFromResponse(response.data) as Message[]) - .catch(() => [] as Message[]); + const alternateRequest = isDirectChatJid(remoteJid) + ? api + .post(url, { + where: { key: { remoteJidAlt: remoteJid } }, + }) + .then((response) => (messageRecordsFromResponse(response.data) as Message[]).filter((message) => messageMatchesConversation(message.key, remoteJid))) + .catch(() => [] as Message[]) + : Promise.resolve([] as Message[]); const [primaryResponse, alternateRecords] = await Promise.all([primaryRequest, alternateRequest]); const primaryRecords = messageRecordsFromResponse(primaryResponse.data) as Message[]; diff --git a/tests/chat-identity.test.ts b/tests/chat-identity.test.ts index 2dce8b7..4370434 100644 --- a/tests/chat-identity.test.ts +++ b/tests/chat-identity.test.ts @@ -1,7 +1,7 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { isDirectChatJid, mergeMessagesByKeyId, messageMatchesConversation, messageRecordsFromResponse } from "../src/lib/chat/identity"; +import { isDirectChatJid, mergeMessagesByKeyId, messageMatchesConversation, messageRecordsFromResponse } from "../src/lib/chat/identity.js"; describe("isDirectChatJid", () => { it("detects phone direct chats", () => { @@ -47,6 +47,19 @@ describe("messageMatchesConversation", () => { true, ); }); + + it("rejects unrelated conversation identities", () => { + assert.equal( + messageMatchesConversation( + { + remoteJid: "15555550123@s.whatsapp.net", + remoteJidAlt: "15555550123@lid", + }, + "5511999999999@s.whatsapp.net", + ), + false, + ); + }); }); describe("mergeMessagesByKeyId", () => { diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 51886df..c5ba946 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -1,14 +1,14 @@ { "compilerOptions": { "target": "ES2022", - "module": "CommonJS", - "moduleResolution": "node", + "module": "NodeNext", + "moduleResolution": "NodeNext", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "types": ["node"], "rootDir": "..", - "outDir": "/tmp/evolution-manager-v2-identity-test", + "outDir": ".tmp/identity-test", "noEmitOnError": true }, "files": ["../src/lib/chat/identity.ts", "./chat-identity.test.ts"] From 23a2c74f07875a182e3845ab645fdeefc3ffafde Mon Sep 17 00:00:00 2001 From: Tom Deckers Date: Wed, 2 Sep 2026 19:56:19 +0200 Subject: [PATCH 3/3] fix: render persisted messages without payload --- package.json | 2 +- src/lib/chat/message-payload.ts | 10 ++++ src/pages/instance/Chat/messages.tsx | 69 +++++++++++++--------------- tests/chat-message-payload.test.ts | 41 +++++++++++++++++ tests/tsconfig.json | 2 +- 5 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 src/lib/chat/message-payload.ts create mode 100644 tests/chat-message-payload.test.ts diff --git a/package.json b/package.json index 36715fc..6772a9b 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "lint:check": "eslint ./src/**/*.{ts,tsx} --ext .ts,.tsx", "type-check": "tsc --noEmit", "preview": "vite preview", - "test": "npm run test:identity", + "test": "tsc -p tests/tsconfig.json && node --test tests/.tmp/identity-test/tests/*.test.js", "test:identity": "tsc -p tests/tsconfig.json && node --test tests/.tmp/identity-test/tests/chat-identity.test.js", "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"" diff --git a/src/lib/chat/message-payload.ts b/src/lib/chat/message-payload.ts new file mode 100644 index 0000000..bb7bf6d --- /dev/null +++ b/src/lib/chat/message-payload.ts @@ -0,0 +1,10 @@ +/** Evolution persists some Android outbound records with messageType set and message null. */ +export const MISSING_CHAT_MESSAGE_FALLBACK = "Message unavailable"; + +export function getChatMessagePayload(message: { message?: T | null } | null | undefined): T | null { + if (message == null || message.message == null) { + return null; + } + + return message.message; +} diff --git a/src/pages/instance/Chat/messages.tsx b/src/pages/instance/Chat/messages.tsx index a27291f..f6064b2 100644 --- a/src/pages/instance/Chat/messages.tsx +++ b/src/pages/instance/Chat/messages.tsx @@ -10,6 +10,7 @@ import { Textarea } from "@/components/ui/textarea"; import { useInstance } from "@/contexts/InstanceContext"; import { messageMatchesConversation } from "@/lib/chat/identity"; +import { getChatMessagePayload, MISSING_CHAT_MESSAGE_FALLBACK } from "@/lib/chat/message-payload"; import { useFindChat } from "@/lib/queries/chat/findChat"; import { useFindMessages } from "@/lib/queries/chat/findMessages"; import { useSendMessage, useSendMedia } from "@/lib/queries/chat/sendMessage"; @@ -113,14 +114,10 @@ const DateSeparator = ({ date }: { date: string }) => ( ); -const formatMessageTime = (date: Date, locale: string): string => - date.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" }); +const formatMessageTime = (date: Date, locale: string): string => date.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" }); // WhatsApp-like deterministic color palette per sender -const SENDER_COLORS = [ - "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", - "#009688", "#4caf50", "#ff9800", "#f44336", "#795548", -]; +const SENDER_COLORS = ["#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#ff9800", "#f44336", "#795548"]; const getSenderColor = (key: string): string => { let hash = 0; @@ -153,11 +150,16 @@ const getMessageText = (messageObj: any): string => { // Component to render different message types based on messageType const MessageContent = ({ message }: { message: Message }) => { const messageType = message.messageType as string; + const payload = getChatMessagePayload(message); + + if (payload == null) { + return {MISSING_CHAT_MESSAGE_FALLBACK}; + } switch (messageType) { case "conversation": - if (message.message.contactMessage) { - const contactMsg = message.message.contactMessage; + if (payload.contactMessage) { + const contactMsg = payload.contactMessage; return (
@@ -170,8 +172,8 @@ const MessageContent = ({ message }: { message: Message }) => { ); } - if (message.message.locationMessage) { - const locationMsg = message.message.locationMessage; + if (payload.locationMessage) { + const locationMsg = payload.locationMessage; return (
@@ -193,16 +195,16 @@ const MessageContent = ({ message }: { message: Message }) => { ); } - return {getMessageText(message.message)}; + return {getMessageText(payload)}; case "extendedTextMessage": - return {message.message.conversation ?? message.message.extendedTextMessage?.text}; + return {payload.conversation ?? payload.extendedTextMessage?.text}; case "imageMessage": // Use base64 data or mediaUrl for images - const imageBase64 = message.message.base64 ? (message.message.base64.startsWith("data:") ? message.message.base64 : `data:image/jpeg;base64,${message.message.base64}`) : null; + const imageBase64 = payload.base64 ? (payload.base64.startsWith("data:") ? payload.base64 : `data:image/jpeg;base64,${payload.base64}`) : null; - const imageSrc = imageBase64 || message.message.mediaUrl; + const imageSrc = imageBase64 || payload.mediaUrl; return (
@@ -224,15 +226,15 @@ const MessageContent = ({ message }: { message: Message }) => {

Missing base64 data and mediaUrl

)} - {message.message.imageMessage?.caption &&

{message.message.imageMessage.caption}

} + {payload.imageMessage?.caption &&

{payload.imageMessage.caption}

}
); case "videoMessage": // Use base64 data or mediaUrl for videos - const videoBase64 = message.message.base64 ? (message.message.base64.startsWith("data:") ? message.message.base64 : `data:video/mp4;base64,${message.message.base64}`) : null; + const videoBase64 = payload.base64 ? (payload.base64.startsWith("data:") ? payload.base64 : `data:video/mp4;base64,${payload.base64}`) : null; - const videoSrc = videoBase64 || message.message.mediaUrl; + const videoSrc = videoBase64 || payload.mediaUrl; return (
@@ -252,15 +254,15 @@ const MessageContent = ({ message }: { message: Message }) => {

Missing base64 data and mediaUrl

)} - {message.message.videoMessage?.caption &&

{message.message.videoMessage.caption}

} + {payload.videoMessage?.caption &&

{payload.videoMessage.caption}

}
); case "audioMessage": // Use base64 data or mediaUrl for audio - const audioBase64 = message.message.base64 ? (message.message.base64.startsWith("data:") ? message.message.base64 : `data:audio/mpeg;base64,${message.message.base64}`) : null; + const audioBase64 = payload.base64 ? (payload.base64.startsWith("data:") ? payload.base64 : `data:audio/mpeg;base64,${payload.base64}`) : null; - const audioSrc = audioBase64 || message.message.mediaUrl; + const audioSrc = audioBase64 || payload.mediaUrl; return audioSrc ? (
); @@ -606,9 +606,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa
- - {formatMessageTime(getMessageTimestamp(message), locale)} - + {formatMessageTime(getMessageTimestamp(message), locale)} ); @@ -637,9 +635,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa {groupedMessages.map((group, groupIndex) => (
- {group.messages.map((message) => - message.key.fromMe ? renderBubbleRight(message) : renderBubbleLeft(message), - )} + {group.messages.map((message) => (message.key.fromMe ? renderBubbleRight(message) : renderBubbleLeft(message)))}
))}
@@ -652,9 +648,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa
)}
-
- {instance && } -
+
{instance && }