Merged in release/2024-11-22 (pull request #1975)
Release/2024 11 22 into test-AIO - IO-3000
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
// Scripts for firebase and firebase messaging
|
// Scripts for firebase and firebase messaging
|
||||||
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js");
|
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js");
|
||||||
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-messaging.js");
|
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-messaging-compat.js");
|
||||||
|
|
||||||
// Initialize the Firebase app in the service worker by passing the generated config
|
// Initialize the Firebase app in the service worker by passing the generated config
|
||||||
let firebaseConfig;
|
let firebaseConfig;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useApolloClient, useQuery } from "@apollo/client";
|
import { gql, useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import SocketContext from "../../contexts/SocketIO/socketContext";
|
import SocketContext from "../../contexts/SocketIO/socketContext";
|
||||||
import { GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries";
|
import { GET_CONVERSATION_DETAILS, CONVERSATION_SUBSCRIPTION_BY_PK } from "../../graphql/conversations.queries";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ChatConversationComponent from "./chat-conversation.component";
|
import ChatConversationComponent from "./chat-conversation.component";
|
||||||
@@ -14,11 +14,12 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const { socket } = useContext(SocketContext);
|
const { socket } = useContext(SocketContext);
|
||||||
const [markingAsReadInProgress, setMarkingAsReadInProgress] = useState(false);
|
const [markingAsReadInProgress, setMarkingAsReadInProgress] = useState(false);
|
||||||
|
|
||||||
|
// Fetch conversation details
|
||||||
const {
|
const {
|
||||||
loading: convoLoading,
|
loading: convoLoading,
|
||||||
error: convoError,
|
error: convoError,
|
||||||
@@ -29,49 +30,85 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
nextFetchPolicy: "network-only"
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateCacheWithReadMessages = useCallback(
|
// Subscription for conversation updates
|
||||||
(conversationId, messageIds) => {
|
useSubscription(CONVERSATION_SUBSCRIPTION_BY_PK, {
|
||||||
if (!conversationId || !messageIds || messageIds.length === 0) return;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// Mark individual messages as read
|
messages.forEach((message) => {
|
||||||
messageIds.forEach((messageId) => {
|
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({
|
client.cache.modify({
|
||||||
id: client.cache.identify({ __typename: "messages", id: messageId }),
|
id: client.cache.identify({ __typename: "conversations", id: selectedConversation }),
|
||||||
fields: {
|
fields: {
|
||||||
read() {
|
messages(existingMessages = []) {
|
||||||
return true; // Mark message as read
|
const alreadyExists = existingMessages.some((msg) => msg.__ref === messageRef);
|
||||||
|
if (alreadyExists) return existingMessages;
|
||||||
|
return [...existingMessages, { __ref: messageRef }];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Update aggregate unread count for the conversation
|
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
||||||
fields: {
|
fields: {
|
||||||
messages_aggregate(existingAggregate) {
|
unreadcnt: () => 0
|
||||||
return {
|
|
||||||
...existingAggregate,
|
|
||||||
aggregate: {
|
|
||||||
...existingAggregate.aggregate,
|
|
||||||
count: 0 // No unread messages remaining
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[client.cache]
|
[client.cache]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle WebSocket events
|
// WebSocket event handlers
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket || !socket.connected) return;
|
if (!socket?.connected) return;
|
||||||
|
|
||||||
const handleConversationChange = (data) => {
|
const handleConversationChange = (data) => {
|
||||||
if (data.type === "conversation-marked-read") {
|
if (data.type === "conversation-marked-read") {
|
||||||
const { conversationId, messageIds } = data;
|
const { conversationId, messageIds } = data;
|
||||||
console.log("Conversation change received:", data);
|
|
||||||
updateCacheWithReadMessages(conversationId, messageIds);
|
updateCacheWithReadMessages(conversationId, messageIds);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -81,11 +118,11 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
return () => {
|
return () => {
|
||||||
socket.off("conversation-changed", handleConversationChange);
|
socket.off("conversation-changed", handleConversationChange);
|
||||||
};
|
};
|
||||||
}, [socket, client, updateCacheWithReadMessages]);
|
}, [socket, updateCacheWithReadMessages]);
|
||||||
|
|
||||||
// Handle joining/leaving conversation
|
// Join and leave conversation via WebSocket
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket || !socket.connected) return;
|
if (!socket?.connected || !selectedConversation || !bodyshop?.id) return;
|
||||||
|
|
||||||
socket.emit("join-bodyshop-conversation", {
|
socket.emit("join-bodyshop-conversation", {
|
||||||
bodyshopId: bodyshop.id,
|
bodyshopId: bodyshop.id,
|
||||||
@@ -98,17 +135,14 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
conversationId: selectedConversation
|
conversationId: selectedConversation
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [selectedConversation, bodyshop, socket]);
|
}, [socket, bodyshop, selectedConversation]);
|
||||||
|
|
||||||
// Handle marking conversation as read
|
// Mark conversation as read
|
||||||
const handleMarkConversationAsRead = async () => {
|
const handleMarkConversationAsRead = async () => {
|
||||||
if (!convoData || !selectedConversation || markingAsReadInProgress) return;
|
if (!convoData || markingAsReadInProgress) return;
|
||||||
|
|
||||||
const conversation = convoData.conversations_by_pk;
|
const conversation = convoData.conversations_by_pk;
|
||||||
if (!conversation) {
|
if (!conversation) return;
|
||||||
console.warn(`No data found for conversation ID: ${selectedConversation}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unreadMessageIds = conversation.messages
|
const unreadMessageIds = conversation.messages
|
||||||
?.filter((message) => !message.read && !message.isoutbound)
|
?.filter((message) => !message.read && !message.isoutbound)
|
||||||
@@ -116,22 +150,16 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
|
|
||||||
if (unreadMessageIds?.length > 0) {
|
if (unreadMessageIds?.length > 0) {
|
||||||
setMarkingAsReadInProgress(true);
|
setMarkingAsReadInProgress(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
await axios.post("/sms/markConversationRead", {
|
||||||
conversation,
|
conversation,
|
||||||
imexshopid: bodyshop?.imexshopid,
|
imexshopid: bodyshop?.imexshopid,
|
||||||
bodyshopid: bodyshop?.id
|
bodyshopid: bodyshop?.id
|
||||||
};
|
});
|
||||||
|
|
||||||
console.log("Marking conversation as read:", payload);
|
|
||||||
|
|
||||||
await axios.post("/sms/markConversationRead", payload);
|
|
||||||
|
|
||||||
// Update local cache
|
|
||||||
updateCacheWithReadMessages(selectedConversation, unreadMessageIds);
|
updateCacheWithReadMessages(selectedConversation, unreadMessageIds);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error marking conversation as read:", error.response?.data || error.message);
|
console.error("Error marking conversation as read:", error.message);
|
||||||
} finally {
|
} finally {
|
||||||
setMarkingAsReadInProgress(false);
|
setMarkingAsReadInProgress(false);
|
||||||
}
|
}
|
||||||
@@ -141,11 +169,11 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
return (
|
return (
|
||||||
<ChatConversationComponent
|
<ChatConversationComponent
|
||||||
subState={[convoLoading, convoError]}
|
subState={[convoLoading, convoError]}
|
||||||
conversation={convoData ? convoData.conversations_by_pk : {}}
|
conversation={convoData?.conversations_by_pk || {}}
|
||||||
messages={convoData ? convoData.conversations_by_pk.messages : []}
|
messages={convoData?.conversations_by_pk?.messages || []}
|
||||||
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(ChatConversationContainer);
|
export default connect(mapStateToProps)(ChatConversationContainer);
|
||||||
|
|||||||
Reference in New Issue
Block a user