diff --git a/client/src/components/chat-affix/registerMessagingSocketHandlers.js b/client/src/components/chat-affix/registerMessagingSocketHandlers.js
index a6c7cc33a..f2d9d1ef9 100644
--- a/client/src/components/chat-affix/registerMessagingSocketHandlers.js
+++ b/client/src/components/chat-affix/registerMessagingSocketHandlers.js
@@ -334,29 +334,75 @@ export const registerMessagingHandlers = ({ socket, client }) => {
break;
case "tag-added":
- client.cache.modify({
- id: client.cache.identify({ __typename: "conversations", id: conversationId }),
- fields: {
- job_conversations: (existing = []) => [...existing, ...job_conversations]
+ // Ensure `job_conversations` is properly formatted
+ const formattedJobConversations = job_conversations.map((jc) => ({
+ __typename: "job_conversations",
+ jobid: jc.jobid || jc.job?.id,
+ conversationid: conversationId,
+ job: jc.job || {
+ __typename: "jobs",
+ id: data.selectedJob.id,
+ ro_number: data.selectedJob.ro_number,
+ ownr_co_nm: data.selectedJob.ownr_co_nm,
+ ownr_fn: data.selectedJob.ownr_fn,
+ ownr_ln: data.selectedJob.ownr_ln
}
- });
- break;
+ }));
- case "tag-removed":
client.cache.modify({
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
fields: {
- job_conversations: (existing = [], { readField }) => {
- return existing.filter((jobRef) => {
- // Read the `jobid` field safely, even if the structure is normalized
- const jobId = readField("jobid", jobRef);
- return jobId !== fields.jobId;
+ job_conversations: (existing = []) => {
+ // Ensure no duplicates based on both `conversationid` and `jobid`
+ const existingLinks = new Set(
+ existing.map((jc) => {
+ const jobId = client.cache.readFragment({
+ id: client.cache.identify(jc),
+ fragment: gql`
+ fragment JobConversationLinkAdded on job_conversations {
+ jobid
+ conversationid
+ }
+ `
+ })?.jobid;
+ return `${jobId}:${conversationId}`; // Unique identifier for a job-conversation link
+ })
+ );
+
+ const newItems = formattedJobConversations.filter((jc) => {
+ const uniqueLink = `${jc.jobid}:${jc.conversationid}`;
+ return !existingLinks.has(uniqueLink);
});
+
+ return [...existing, ...newItems];
}
}
});
+
break;
+ case "tag-removed":
+ try {
+ const conversationCacheId = client.cache.identify({ __typename: "conversations", id: conversationId });
+
+ // Evict the specific cache entry for job_conversations
+ client.cache.evict({
+ id: conversationCacheId,
+ fieldName: "job_conversations"
+ });
+
+ // Garbage collect evicted entries
+ client.cache.gc();
+
+ logLocal("handleConversationChanged - tag removed - Refetched conversation list after state change", {
+ conversationId,
+ type
+ });
+ } catch (error) {
+ console.error("Error refetching queries after conversation state change: (Tag Removed)", error);
+ }
+
+ break;
default:
logLocal("handleConversationChanged - Unhandled type", { type });
client.cache.modify({
diff --git a/client/src/components/chat-messages-list/chat-message-list.component.jsx b/client/src/components/chat-messages-list/chat-message-list.component.jsx
index f045cae8c..3f2bba2f0 100644
--- a/client/src/components/chat-messages-list/chat-message-list.component.jsx
+++ b/client/src/components/chat-messages-list/chat-message-list.component.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useState } from "react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
import { Virtuoso } from "react-virtuoso";
import { renderMessage } from "./renderMessage";
import "./chat-message-list.styles.scss";
@@ -16,7 +16,7 @@ export default function ChatMessageListComponent({ messages }) {
loadedImagesRef.current = 0;
};
- const preloadImages = (imagePaths, onComplete) => {
+ const preloadImages = useCallback((imagePaths, onComplete) => {
resetImageLoadState();
if (imagePaths.length === 0) {
@@ -34,7 +34,7 @@ export default function ChatMessageListComponent({ messages }) {
}
};
});
- };
+ }, []);
// Ensure all images are loaded on initial render
useEffect(() => {
@@ -51,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) {
});
}
});
- }, [messages]);
+ }, [messages, preloadImages]);
// Handle scrolling when new messages are added
useEffect(() => {
@@ -69,7 +69,7 @@ export default function ChatMessageListComponent({ messages }) {
});
}
});
- }, [messages, atBottom]);
+ }, [messages, atBottom, preloadImages]);
return (
diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx
index 443dfde3e..96a87885e 100644
--- a/client/src/components/chat-popup/chat-popup.component.jsx
+++ b/client/src/components/chat-popup/chat-popup.component.jsx
@@ -1,11 +1,11 @@
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
-import { useApolloClient, useLazyQuery } from "@apollo/client";
+import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import React, { useContext, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
-import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
+import { CONVERSATION_LIST_QUERY, UNREAD_CONVERSATION_COUNT } from "../../graphql/conversations.queries";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { selectChatVisible, selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
@@ -38,6 +38,14 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
...(pollInterval > 0 ? { pollInterval } : {})
});
+ // Query for unread count when chat is not visible
+ const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
+ fetchPolicy: "network-only",
+ nextFetchPolicy: "network-only",
+ skip: chatVisible, // Skip when chat is visible
+ ...(pollInterval > 0 ? { pollInterval } : {})
+ });
+
// Socket connection status
useEffect(() => {
const handleSocketStatus = () => {
@@ -77,23 +85,29 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
// Get unread count from the cache
const unreadCount = (() => {
- try {
- const cachedData = client.readQuery({
- query: CONVERSATION_LIST_QUERY,
- variables: { offset: 0 }
- });
+ if (chatVisible) {
+ try {
+ const cachedData = client.readQuery({
+ query: CONVERSATION_LIST_QUERY,
+ variables: { offset: 0 }
+ });
- if (!cachedData?.conversations) return 0;
+ if (!cachedData?.conversations) return 0;
- // Aggregate unread message count
- return cachedData.conversations.reduce((total, conversation) => {
- const unread = conversation.messages_aggregate?.aggregate?.count || 0;
- return total + unread;
- }, 0);
- } catch (error) {
- console.warn("Unread count not found in cache:", error);
- return 0; // Fallback if not in cache
+ // Aggregate unread message count
+ return cachedData.conversations.reduce((total, conversation) => {
+ const unread = conversation.messages_aggregate?.aggregate?.count || 0;
+ return total + unread;
+ }, 0);
+ } catch (error) {
+ console.warn("Unread count not found in cache:", error);
+ return 0; // Fallback if not in cache
+ }
+ } else if (unreadData?.messages_aggregate?.aggregate?.count) {
+ // Use the unread count from the query result
+ return unreadData.messages_aggregate.aggregate.count;
}
+ return 0;
})();
return (
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 1bb791b00..63839b263 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
@@ -52,20 +52,26 @@ export function ChatTagRoContainer({ conversation, bodyshop }) {
// 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]
+ selectedJob,
+ job_conversations: [
+ {
+ __typename: "job_conversations",
+ jobid: selectedJob.id,
+ conversationid: conversation.id,
+ job: {
+ __typename: "jobs",
+ id: selectedJob.id,
+ ro_number: selectedJob.ro_number,
+ ownr_co_nm: selectedJob.ownr_co_nm,
+ ownr_fn: selectedJob.ownr_fn,
+ ownr_ln: selectedJob.ownr_ln
+ }
+ }
+ ]
});
}
diff --git a/client/src/utils/GraphQLClient.js b/client/src/utils/GraphQLClient.js
index c60545f02..689083139 100644
--- a/client/src/utils/GraphQLClient.js
+++ b/client/src/utils/GraphQLClient.js
@@ -3,7 +3,7 @@ import { setContext } from "@apollo/client/link/context";
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
import { RetryLink } from "@apollo/client/link/retry";
import { WebSocketLink } from "@apollo/client/link/ws";
-import { getMainDefinition, offsetLimitPagination } from "@apollo/client/utilities";
+import { getMainDefinition } from "@apollo/client/utilities";
//import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger";
//import axios from "axios";
@@ -143,36 +143,7 @@ middlewares.push(
new SentryLink().concat(roundTripLink.concat(retryLink.concat(errorLink.concat(authLink.concat(link)))))
);
-const cache = new InMemoryCache({
- typePolicies: {
- conversations: {
- fields: {
- job_conversations: {
- merge(existing = [], incoming = [], { readField }) {
- const merged = new Map();
-
- // Add existing data to the map
- existing.forEach((jobConversation) => {
- // Use `readField` to get the unique `jobid`, fallback to `__ref`
- const jobId = readField("jobid", jobConversation) || jobConversation.__ref;
- if (jobId) merged.set(jobId, jobConversation);
- });
-
- // Add or replace with incoming data
- incoming.forEach((jobConversation) => {
- // Use `readField` to get the unique `jobid`, fallback to `__ref`
- const jobId = readField("jobid", jobConversation) || jobConversation.__ref;
- if (jobId) merged.set(jobId, jobConversation);
- });
-
- // Return the merged data as an array
- return Array.from(merged.values());
- }
- }
- }
- }
- }
-});
+const cache = new InMemoryCache({});
const client = new ApolloClient({
link: ApolloLink.from(middlewares),
cache,