From 8d6fba2b6103004898ace0e833f96d6f3be86e43 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 27 Nov 2024 11:27:34 -0800 Subject: [PATCH] feature/IO-3000-messaging-sockets-migration2 - -misc Signed-off-by: Dave Richer --- .../registerMessagingSocketHandlers.js | 145 ++++++------------ .../chat-conversation-list.component.jsx | 12 +- ...chat-conversation-title-tags.component.jsx | 25 +-- .../chat-tag-ro/chat-tag-ro.container.jsx | 49 +++--- 4 files changed, 95 insertions(+), 136 deletions(-) diff --git a/client/src/components/chat-affix/registerMessagingSocketHandlers.js b/client/src/components/chat-affix/registerMessagingSocketHandlers.js index 09b405861..895fa9db1 100644 --- a/client/src/components/chat-affix/registerMessagingSocketHandlers.js +++ b/client/src/components/chat-affix/registerMessagingSocketHandlers.js @@ -2,39 +2,65 @@ import { CONVERSATION_LIST_QUERY, GET_CONVERSATION_DETAILS } from "../../graphql import { gql } from "@apollo/client"; const logLocal = (message, ...args) => { - if (import.meta.env.PROD) { - return; + if (import.meta.env.VITE_APP_IS_TEST || !import.meta.env.PROD) { + console.log(`==================== ${message} ====================`); + console.dir({ ...args }); } - console.log(`==================== ${message} ====================`); - console.dir({ ...args }); }; +// Utility function to enrich conversation data +const enrichConversation = (conversation, isOutbound) => ({ + ...conversation, + updated_at: conversation.updated_at || new Date().toISOString(), + unreadcnt: conversation.unreadcnt || 0, + archived: conversation.archived || false, + label: conversation.label || null, + job_conversations: conversation.job_conversations || [], + messages_aggregate: conversation.messages_aggregate || { + __typename: "messages_aggregate", + aggregate: { + __typename: "messages_aggregate_fields", + count: isOutbound ? 0 : 1 + } + }, + __typename: "conversations" +}); + export const registerMessagingHandlers = ({ socket, client }) => { if (!(socket && client)) return; const handleNewMessageSummary = async (message) => { const { conversationId, newConversation, existingConversation, isoutbound } = message; + logLocal("handleNewMessageSummary - Start", { message, isNew: !existingConversation }); const queryVariables = { offset: 0 }; - // Utility function to enrich conversation data - const enrichConversation = (conversation, isOutbound) => ({ - ...conversation, - updated_at: conversation.updated_at || new Date().toISOString(), - unreadcnt: conversation.unreadcnt || 0, - archived: conversation.archived || false, - label: conversation.label || null, - job_conversations: conversation.job_conversations || [], - messages_aggregate: conversation.messages_aggregate || { - __typename: "messages_aggregate", - aggregate: { - __typename: "messages_aggregate_fields", - count: isOutbound ? 0 : 1 + if (!existingConversation && conversationId) { + // Attempt to read from the cache to determine if this is actually a new conversation + try { + const cachedConversation = client.cache.readFragment({ + id: client.cache.identify({ __typename: "conversations", id: conversationId }), + fragment: gql` + fragment ExistingConversationCheck on conversations { + id + } + ` + }); + + if (cachedConversation) { + logLocal("handleNewMessageSummary - Existing Conversation inferred from cache", { + conversationId + }); + return handleNewMessageSummary({ + ...message, + existingConversation: true + }); } - }, - __typename: "conversations" - }); + } catch (error) { + logLocal("handleNewMessageSummary - Cache miss", { conversationId }); + } + } // Handle new conversation if (!existingConversation && newConversation?.phone_num) { @@ -49,7 +75,6 @@ export const registerMessagingHandlers = ({ socket, client }) => { const existingConversations = queryResults?.conversations || []; const enrichedConversation = enrichConversation(newConversation, isoutbound); - // Avoid adding duplicate conversations if (!existingConversations.some((conv) => conv.id === enrichedConversation.id)) { client.cache.modify({ id: "ROOT_QUERY", @@ -68,81 +93,7 @@ export const registerMessagingHandlers = ({ socket, client }) => { // Handle existing conversation if (existingConversation) { - let conversationDetails; - - // Attempt to read existing conversation details from cache try { - conversationDetails = client.cache.readFragment({ - id: client.cache.identify({ __typename: "conversations", id: conversationId }), - fragment: gql` - fragment ExistingConversation on conversations { - id - phone_num - updated_at - archived - label - unreadcnt - job_conversations { - jobid - conversationid - } - messages_aggregate { - aggregate { - count - } - } - __typename - } - ` - }); - } catch (error) { - logLocal("handleNewMessageSummary - Cache miss for conversation, fetching from server", { conversationId }); - } - - // Fetch conversation details from server if not in cache - if (!conversationDetails) { - try { - const { data } = await client.query({ - query: GET_CONVERSATION_DETAILS, - variables: { conversationId }, - fetchPolicy: "network-only" - }); - conversationDetails = data?.conversations_by_pk; - } catch (error) { - console.error("Failed to fetch conversation details from server:", error); - return; - } - } - - // Validate that conversation details were retrieved - if (!conversationDetails) { - console.error("Unable to retrieve conversation details. Skipping cache update."); - return; - } - - try { - // Check if the conversation is already in the cache - const queryResults = client.cache.readQuery({ - query: CONVERSATION_LIST_QUERY, - variables: queryVariables - }); - - const isAlreadyInCache = queryResults?.conversations.some((conv) => conv.id === conversationId); - - if (!isAlreadyInCache) { - const enrichedConversation = enrichConversation(conversationDetails, isoutbound); - - client.cache.modify({ - id: "ROOT_QUERY", - fields: { - conversations(existingConversations = []) { - return [enrichedConversation, ...existingConversations]; - } - } - }); - } - - // Update fields for the existing conversation in the cache client.cache.modify({ id: client.cache.identify({ __typename: "conversations", id: conversationId }), fields: { @@ -166,7 +117,11 @@ export const registerMessagingHandlers = ({ socket, client }) => { } catch (error) { console.error("Error updating cache for existing conversation:", error); } + + return; } + + logLocal("New Conversation Summary finished without work", { message }); }; const handleNewMessageDetailed = (message) => { diff --git a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx index 2aa11757d..16d4c0bf1 100644 --- a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx +++ b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx @@ -32,8 +32,13 @@ function ChatConversationListComponent({ conversationList, selectedConversation, return () => clearInterval(interval); // Cleanup on unmount }, []); + // Memoize the sorted conversation list + const sortedConversationList = React.useMemo(() => { + return _.orderBy(conversationList, ["updated_at"], ["desc"]); + }, [conversationList]); + const renderConversation = (index) => { - const item = conversationList[index]; + const item = sortedConversationList[index]; const cardContentRight = {item.updated_at}; const cardContentLeft = item.job_conversations.length > 0 @@ -76,13 +81,10 @@ function ChatConversationListComponent({ conversationList, selectedConversation, ); }; - // CAN DO: Can go back into virtuoso for additional fetch - // endReached={loadMoreConversations} // Calls loadMoreConversations when scrolled to the bottom - return (
renderConversation(index)} style={{ height: "100%", width: "100%" }} /> diff --git a/client/src/components/chat-conversation-title-tags/chat-conversation-title-tags.component.jsx b/client/src/components/chat-conversation-title-tags/chat-conversation-title-tags.component.jsx index 821ad1603..4203e8f5b 100644 --- a/client/src/components/chat-conversation-title-tags/chat-conversation-title-tags.component.jsx +++ b/client/src/components/chat-conversation-title-tags/chat-conversation-title-tags.component.jsx @@ -20,10 +20,10 @@ export function ChatConversationTitleTags({ jobConversations, bodyshop }) { const [removeJobConversation] = useMutation(REMOVE_CONVERSATION_TAG); const { socket } = useContext(SocketContext); - const handleRemoveTag = (jobId) => { + const handleRemoveTag = async (jobId) => { const convId = jobConversations[0].conversationid; if (!!convId) { - removeJobConversation({ + await removeJobConversation({ variables: { conversationId: convId, jobId: jobId @@ -38,17 +38,18 @@ export function ChatConversationTitleTags({ jobConversations, bodyshop }) { } }); } - }).then(() => { - if (socket) { - // Emit the `conversation-modified` event - socket.emit("conversation-modified", { - bodyshopId: bodyshop.id, - conversationId: convId, - type: "tag-removed", - jobId: jobId - }); - } }); + + if (socket) { + // Emit the `conversation-modified` event + socket.emit("conversation-modified", { + bodyshopId: bodyshop.id, + conversationId: convId, + type: "tag-removed", + jobId: jobId + }); + } + logImEXEvent("messaging_remove_job_tag", { conversationId: convId, jobId: jobId diff --git a/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx b/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx index 8d3476c7d..88c25bf81 100644 --- a/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx +++ b/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx @@ -28,7 +28,7 @@ export function ChatTagRoContainer({ conversation, bodyshop }) { const executeSearch = (v) => { logImEXEvent("messaging_search_job_tag", { searchTerm: v }); - loadRo(v); + loadRo(v).catch((e) => console.error("Error in ChatTagRoContainer executeSearch:", e)); }; const debouncedExecuteSearch = _.debounce(executeSearch, 500); @@ -41,33 +41,34 @@ export function ChatTagRoContainer({ conversation, bodyshop }) { variables: { conversationId: conversation.id } }); - const handleInsertTag = (value, option) => { + const handleInsertTag = async (value, option) => { logImEXEvent("messaging_add_job_tag"); - insertTag({ + await insertTag({ variables: { jobId: option.key } - }).then(() => { - if (socket) { - // Find the job details from the search data - const selectedJob = data?.search_jobs.find((job) => job.id === option.key); - if (!selectedJob) return; - const newJobConversation = { - __typename: "job_conversations", - jobid: selectedJob.id, - conversationid: conversation.id, - job: { - __typename: "jobs", - ...selectedJob - } - }; - socket.emit("conversation-modified", { - conversationId: conversation.id, - bodyshopId: bodyshop.id, - type: "tag-added", - job_conversations: [newJobConversation] - }); - } }); + + if (socket) { + // Find the job details from the search data + const selectedJob = data?.search_jobs.find((job) => job.id === option.key); + if (!selectedJob) return; + const newJobConversation = { + __typename: "job_conversations", + jobid: selectedJob.id, + conversationid: conversation.id, + job: { + __typename: "jobs", + ...selectedJob + } + }; + socket.emit("conversation-modified", { + conversationId: conversation.id, + bodyshopId: bodyshop.id, + type: "tag-added", + job_conversations: [newJobConversation] + }); + } + setOpen(false); };