import { gql, useApolloClient, useQuery, useSubscription } from "@apollo/client"; import axios from "axios"; import { useCallback, useEffect, useState } from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { CONVERSATION_SUBSCRIPTION_BY_PK, GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries"; import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import ChatConversationComponent from "./chat-conversation.component"; import { useSocket } from "../../contexts/SocketIO/useSocket.js"; const mapStateToProps = createStructuredSelector({ selectedConversation: selectSelectedConversation, bodyshop: selectBodyshop }); function ChatConversationContainer({ bodyshop, selectedConversation }) { const client = useApolloClient(); const { socket } = useSocket(); const [markingAsReadInProgress, setMarkingAsReadInProgress] = useState(false); // Fetch conversation details const { loading: convoLoading, error: convoError, data: convoData } = useQuery(GET_CONVERSATION_DETAILS, { variables: { conversationId: selectedConversation }, fetchPolicy: "network-only", nextFetchPolicy: "network-only" }); // Subscription for conversation updates useSubscription(CONVERSATION_SUBSCRIPTION_BY_PK, { skip: socket?.connected, variables: { conversationId: selectedConversation }, onData: ({ data: subscriptionResult, client }) => { // Extract the messages array from the result const messages = subscriptionResult?.data?.messages; if (!messages || messages.length === 0) { console.warn("No messages found in subscription result."); return; } messages.forEach((message) => { const messageRef = client.cache.identify(message); // Write the new message to the cache client.cache.writeFragment({ id: messageRef, fragment: gql` fragment NewMessage on messages { id status text isoutbound image image_path userid created_at read } `, data: message }); // Update the conversation cache to include the new message client.cache.modify({ id: client.cache.identify({ __typename: "conversations", id: selectedConversation }), fields: { messages(existingMessages = []) { const alreadyExists = existingMessages.some((msg) => msg.__ref === messageRef); if (alreadyExists) return existingMessages; return [...existingMessages, { __ref: messageRef }]; }, updated_at() { return message.created_at; } } }); }); } }); const updateCacheWithReadMessages = useCallback( (conversationId, messageIds) => { if (!conversationId || !messageIds?.length) return; messageIds.forEach((messageId) => { client.cache.modify({ id: client.cache.identify({ __typename: "messages", id: messageId }), fields: { read: () => true } }); }); }, [client.cache] ); // WebSocket event handlers useEffect(() => { if (!socket?.connected) return; const handleConversationChange = (data) => { if (data.type === "conversation-marked-read") { const { conversationId, messageIds } = data; updateCacheWithReadMessages(conversationId, messageIds); } }; socket.on("conversation-changed", handleConversationChange); return () => { socket.off("conversation-changed", handleConversationChange); }; }, [socket, updateCacheWithReadMessages]); // Join and leave conversation via WebSocket useEffect(() => { if (!socket?.connected || !selectedConversation || !bodyshop?.id) return; socket.emit("join-bodyshop-conversation", { bodyshopId: bodyshop.id, conversationId: selectedConversation }); return () => { socket.emit("leave-bodyshop-conversation", { bodyshopId: bodyshop.id, conversationId: selectedConversation }); }; }, [socket, bodyshop, selectedConversation]); // Mark conversation as read const handleMarkConversationAsRead = async () => { if (!convoData || markingAsReadInProgress) return; const conversation = convoData.conversations_by_pk; if (!conversation) return; const unreadMessageIds = conversation.messages ?.filter((message) => !message.read && !message.isoutbound) .map((message) => message.id); if (unreadMessageIds?.length > 0) { setMarkingAsReadInProgress(true); try { await axios.post("/sms/markConversationRead", { conversation, imexshopid: bodyshop?.imexshopid, bodyshopid: bodyshop?.id }); updateCacheWithReadMessages(selectedConversation, unreadMessageIds); } catch (error) { console.error("Error marking conversation as read:", error.message); } finally { setMarkingAsReadInProgress(false); } } }; return ( ); } export default connect(mapStateToProps)(ChatConversationContainer);