diff --git a/backend/src/controllers/messageController.js b/backend/src/controllers/messageController.js index c7561b2..b6ba80f 100644 --- a/backend/src/controllers/messageController.js +++ b/backend/src/controllers/messageController.js @@ -180,6 +180,11 @@ export const updateConversationStatus = asyncHandler(async (req, res) => { conversation.status = status; await conversation.save(); + await conversation.populate([ + { path: 'reporter', select: 'name _id' }, + { path: 'finder', select: 'name _id' }, + ]); + res.status(200).json({ success: true, data: conversation, diff --git a/frontend/src/ItemDetailPage/ChatPanel.jsx b/frontend/src/ItemDetailPage/ChatPanel.jsx index 7db25ae..4401493 100644 --- a/frontend/src/ItemDetailPage/ChatPanel.jsx +++ b/frontend/src/ItemDetailPage/ChatPanel.jsx @@ -29,8 +29,8 @@ export default function ChatPanel({ itemId, conversationId: initialConvId, curre async function loadChat() { try { let convIdToLoad = activeConvId; - - // If finder and no convId, initiate or get existing + + // If finder and no convId, initiate or get existing conversation if (!isReporter && !convIdToLoad) { const initRes = await axios.post(`${API_BASE_URL}/messages/conversations`, { itemId }, { headers: { Authorization: `Bearer ${token}` } @@ -39,7 +39,6 @@ export default function ChatPanel({ itemId, conversationId: initialConvId, curre setActiveConvId(convIdToLoad) setConversation(initRes.data.data) } else if (convIdToLoad) { - // Fetch user conversations to find this one and update state const convsRes = await axios.get(`${API_BASE_URL}/messages/conversations`, { headers: { Authorization: `Bearer ${token}` } }) @@ -49,29 +48,23 @@ export default function ChatPanel({ itemId, conversationId: initialConvId, curre if (!convIdToLoad) return - // Load messages const msgRes = await axios.get(`${API_BASE_URL}/messages/conversations/${convIdToLoad}`, { headers: { Authorization: `Bearer ${token}` } }) const loaded = msgRes.data.data.messages || [] setMessages(loaded) - - // Mark incoming messages as read - loaded.forEach((msg) => { - if (msg.receiver?._id === currentUserId && !msg.isRead) { - socket?.emit('mark_read', { messageId: msg._id, conversationId: convIdToLoad }) - } - }) } catch (err) { console.error('Failed to load chat:', err) - setError('Failed to load chat history.') + setError('Failed to load chat.') } } loadChat() - }, [itemId, isReporter, currentUserId, socket, activeConvId]) + // socket intentionally omitted — mark-reads are handled in the socket effect below + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [itemId, isReporter, currentUserId, activeConvId]) - // Socket Events + // Socket Events — join room, attach listeners, and mark loaded messages as read useEffect(() => { if (!socket || !activeConvId) return @@ -135,6 +128,16 @@ export default function ChatPanel({ itemId, conversationId: initialConvId, curre } }, [socket, activeConvId, currentUserId]) + // Mark already-loaded unread messages as read whenever messages or socket change + useEffect(() => { + if (!socket || !activeConvId || messages.length === 0) return + messages.forEach((msg) => { + if (msg.receiver?._id === currentUserId && !msg.isRead) { + socket.emit('mark_read', { messageId: msg._id, conversationId: activeConvId }) + } + }) + }, [messages, socket, currentUserId, activeConvId]) + useEffect(() => { scrollToBottom() }, [messages, isOtherTyping]) @@ -211,8 +214,11 @@ export default function ChatPanel({ itemId, conversationId: initialConvId, curre if (!activeConvId || !conversation) { return ( -
Loading chat...
+Loading chat...
+ }