socket.on(
"send_message",
async (data: { conversation_id: string; text: string }, ack?: (res: unknown) => void) => {
try {
// Verify user is participant
const conversation = await Conversation.findOne({
_id: data.conversation_id,
participants: user.userId,
});
if (!conversation) {
if (ack) ack({ success: false, message: "Conversation not found" });
return;
}
// Persist to DB
const message = await Message.create({
conversation_id: data.conversation_id,
sender_id: user.userId,
text: data.text,
is_read: false,
});
// Update last_message snapshot
await Conversation.findByIdAndUpdate(data.conversation_id, {
last_message: {
text: data.text,
sender_id: user.userId,
sent_at: message.sent_at,
is_read: false,
},
updated_at: new Date(),
});
const populated = await message.populate("sender_id", "full_name profile_picture_url");
// Emit to all in the conversation room
io.to(`conv:${data.conversation_id}`).emit("new_message", populated);
// Also notify recipient's personal room (for badge/notification)
const recipientId = conversation.participants.find(
(p) => p.toString() !== user.userId
);
if (recipientId) {
io.to(`user:${recipientId}`).emit("message_notification", {
conversation_id: data.conversation_id,
sender: user.userId,
preview: data.text.substring(0, 60),
});
}
if (ack) ack({ success: true, data: populated });
} catch (err) {
console.error("Socket send_message error:", err);
if (ack) ack({ success: false, message: "Error sending message" });
}
}
);