feature/IO-3000-messaging-sockets-migrations2 -

- Checkpoint

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-11-21 11:32:43 -08:00
parent 15151cb4ac
commit 5392659db6
7 changed files with 100 additions and 26 deletions

View File

@@ -1,35 +1,45 @@
import { CONVERSATION_LIST_QUERY, GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries"; import { CONVERSATION_LIST_QUERY, GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries";
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
const logLocal = (message, ...args) => {
if (import.meta.env.PROD) {
return;
}
console.log(`==================== ${message} ====================`);
console.dir({ ...args });
};
export const registerMessagingHandlers = ({ socket, client }) => { export const registerMessagingHandlers = ({ socket, client }) => {
if (!(socket && client)) return; if (!(socket && client)) return;
const handleNewMessageSummary = (message) => { const handleNewMessageSummary = (message) => {
const { conversationId, newConversation, existingConversation, isoutbound } = message; const { conversationId, newConversation, existingConversation, isoutbound } = message;
logLocal("handleNewMessageSummary", message);
if (!existingConversation && newConversation?.phone_num) { if (!existingConversation && newConversation?.phone_num) {
const queryResults = client.cache.readQuery({ const queryResults = client.cache.readQuery({
query: CONVERSATION_LIST_QUERY, query: CONVERSATION_LIST_QUERY,
variables: { offset: 0 } variables: { offset: 0 }
}); });
const fullConversation = {
...newConversation,
updated_at: newConversation.updated_at || new Date().toISOString(),
unreadcnt: newConversation.unreadcnt || 0,
archived: newConversation.archived || false,
label: newConversation.label || null,
job_conversations: newConversation.job_conversations || [],
messages_aggregate: newConversation.messages_aggregate || {
aggregate: { count: isoutbound ? 0 : 1 }
}
};
client.cache.writeQuery({ client.cache.writeQuery({
query: CONVERSATION_LIST_QUERY, query: CONVERSATION_LIST_QUERY,
variables: { offset: 0 }, variables: { offset: 0 },
data: { data: {
conversations: [fullConversation, ...(queryResults?.conversations || [])] conversations: [
{
...newConversation,
updated_at: newConversation.updated_at || new Date().toISOString(),
unreadcnt: newConversation.unreadcnt || 0,
archived: newConversation.archived || false,
label: newConversation.label || null,
job_conversations: newConversation.job_conversations || [],
messages_aggregate: newConversation.messages_aggregate || {
aggregate: { count: isoutbound ? 0 : 1 }
}
},
...(queryResults?.conversations || [])
]
} }
}); });
} else { } else {
@@ -51,9 +61,12 @@ export const registerMessagingHandlers = ({ socket, client }) => {
}); });
} }
}; };
const handleNewMessageDetailed = (message) => { const handleNewMessageDetailed = (message) => {
const { conversationId, newMessage } = message; const { conversationId, newMessage } = message;
logLocal("handleNewMessageDetailed", message);
// Append the new message to the conversation's message list // Append the new message to the conversation's message list
const queryResults = client.cache.readQuery({ const queryResults = client.cache.readQuery({
query: GET_CONVERSATION_DETAILS, query: GET_CONVERSATION_DETAILS,
@@ -76,6 +89,10 @@ export const registerMessagingHandlers = ({ socket, client }) => {
}; };
const handleMessageChanged = (message) => { const handleMessageChanged = (message) => {
if (!message) return;
logLocal("handleMessageChanged", message);
client.cache.modify({ client.cache.modify({
id: client.cache.identify({ __typename: "conversations", id: message.conversationid }), id: client.cache.identify({ __typename: "conversations", id: message.conversationid }),
fields: { fields: {
@@ -118,8 +135,12 @@ export const registerMessagingHandlers = ({ socket, client }) => {
}; };
const handleConversationChanged = (data) => { const handleConversationChanged = (data) => {
if (!data) return;
const { conversationId, type, job_conversations, ...fields } = data; const { conversationId, type, job_conversations, ...fields } = data;
logLocal("handleConversationChanged", data);
// Identify the conversation in the Apollo cache // Identify the conversation in the Apollo cache
const cacheId = client.cache.identify({ const cacheId = client.cache.identify({
__typename: "conversations", __typename: "conversations",
@@ -161,23 +182,60 @@ export const registerMessagingHandlers = ({ socket, client }) => {
}; };
const handleNewMessage = ({ conversationId, message }) => { const handleNewMessage = ({ conversationId, message }) => {
if (!conversationId || !message.id || !message.text) {
return;
}
logLocal("handleNewMessage", { conversationId, message });
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(existing = []) { messages(existing = []) {
// Ensure that the `message` object matches the schema
const newMessageRef = client.cache.writeFragment({ const newMessageRef = client.cache.writeFragment({
data: message, data: {
__typename: "messages",
id: message.id,
body: message.text,
selectedMedia: message.image_path || [],
imexshopid: message.userid,
status: message.status,
created_at: message.created_at,
read: message.read
},
fragment: gql` fragment: gql`
fragment NewMessage on messages { fragment NewMessage on messages {
id id
body body
createdAt
selectedMedia selectedMedia
imexshopid imexshopid
status
created_at
read
} }
` `
}); });
return [...existing, newMessageRef];
// Prevent duplicates by checking if the message already exists
const isDuplicate = existing.some(
(msgRef) =>
client.cache.readFragment({
id: msgRef.__ref,
fragment: gql`
fragment CheckMessage on messages {
id
}
`
})?.id === message.id
);
// We already have it, so return the existing list
if (isDuplicate) {
return existing;
}
return [...existing, newMessageRef]; // Add the new message reference
} }
} }
}); });

View File

@@ -1,21 +1,31 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button } from "antd"; import { Button } from "antd";
import React, { useState } from "react"; import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { TOGGLE_CONVERSATION_ARCHIVE } from "../../graphql/conversations.queries"; import { TOGGLE_CONVERSATION_ARCHIVE } from "../../graphql/conversations.queries";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
export default function ChatArchiveButton({ conversation }) { export default function ChatArchiveButton({ conversation, bodyshop }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE); const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE);
const { socket } = useContext(SocketContext);
const handleToggleArchive = async () => { const handleToggleArchive = async () => {
setLoading(true); setLoading(true);
await updateConversation({ const updatedConversation = await updateConversation({
variables: { id: conversation.id, archived: !conversation.archived }, variables: { id: conversation.id, archived: !conversation.archived }
refetchQueries: ["CONVERSATION_LIST_QUERY"]
}); });
if (socket) {
socket.emit("conversation-modified", {
conversationId: conversation.id,
bodyshopId: bodyshop.id,
archived: updatedConversation.data.update_conversations_by_pk.archived
});
}
setLoading(false); setLoading(false);
}; };

View File

@@ -18,7 +18,7 @@ export default function ChatConversationTitle({ conversation, bodyshop }) {
bodyshop={bodyshop} bodyshop={bodyshop}
/> />
<ChatTagRoContainer conversation={conversation || []} bodyshop={bodyshop} /> <ChatTagRoContainer conversation={conversation || []} bodyshop={bodyshop} />
<ChatArchiveButton conversation={conversation} /> <ChatArchiveButton conversation={conversation} bodyshop={bodyshop} />
</Space> </Space>
); );
} }

View File

@@ -26,7 +26,6 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only" nextFetchPolicy: "network-only"
}); });
const { socket } = useContext(SocketContext); const { socket } = useContext(SocketContext);
useEffect(() => { useEffect(() => {

View File

@@ -50,10 +50,11 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
}; };
sendMessage(newMessage); sendMessage(newMessage);
if (socket) { if (socket) {
const lastMessage = conversation.messages?.[conversation.messages.length - 1]; // Get the last message
socket.emit("message-added", { socket.emit("message-added", {
conversationId: conversation.id, conversationId: conversation.id,
bodyshopId: bodyshop.id, bodyshopId: bodyshop.id,
message: newMessage message: lastMessage
}); });
} }
setSelectedMedia( setSelectedMedia(

View File

@@ -15,6 +15,11 @@ exports.status = async (req, res) => {
} = req; } = req;
try { try {
// Ignore status 'queued'
if (SmsStatus === "queued") {
return res.status(200).json({ message: "Status 'queued' disregarded." });
}
// Update message status in the database // Update message status in the database
const response = await client.request(queries.UPDATE_MESSAGE_STATUS, { const response = await client.request(queries.UPDATE_MESSAGE_STATUS, {
msid: SmsSid, msid: SmsSid,
@@ -47,6 +52,7 @@ exports.status = async (req, res) => {
warning: "No message returned from the database update." warning: "No message returned from the database update."
}); });
} }
res.sendStatus(200); res.sendStatus(200);
} catch (error) { } catch (error) {
logger.log("sms-status-update-error", "ERROR", "api", null, { logger.log("sms-status-update-error", "ERROR", "api", null, {

View File

@@ -181,7 +181,7 @@ const redisSocketEvents = ({
const messageAdded = ({ bodyshopId, conversationId, message }) => { const messageAdded = ({ bodyshopId, conversationId, message }) => {
try { try {
const room = getBodyshopConversationRoom({ bodyshopId, conversationId }); const room = getBodyshopConversationRoom({ bodyshopId, conversationId });
io.to(room).emit("new-message", message); io.to(room).emit("new-message", { message, conversationId });
} catch (error) { } catch (error) {
logger.log("Failed to handle new message", "error", "io-redis", null, { logger.log("Failed to handle new message", "error", "io-redis", null, {
error: error.message, error: error.message,