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 { 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 }) => {
if (!(socket && client)) return;
const handleNewMessageSummary = (message) => {
const { conversationId, newConversation, existingConversation, isoutbound } = message;
logLocal("handleNewMessageSummary", message);
if (!existingConversation && newConversation?.phone_num) {
const queryResults = client.cache.readQuery({
query: CONVERSATION_LIST_QUERY,
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({
query: CONVERSATION_LIST_QUERY,
variables: { offset: 0 },
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 {
@@ -51,9 +61,12 @@ export const registerMessagingHandlers = ({ socket, client }) => {
});
}
};
const handleNewMessageDetailed = (message) => {
const { conversationId, newMessage } = message;
logLocal("handleNewMessageDetailed", message);
// Append the new message to the conversation's message list
const queryResults = client.cache.readQuery({
query: GET_CONVERSATION_DETAILS,
@@ -76,6 +89,10 @@ export const registerMessagingHandlers = ({ socket, client }) => {
};
const handleMessageChanged = (message) => {
if (!message) return;
logLocal("handleMessageChanged", message);
client.cache.modify({
id: client.cache.identify({ __typename: "conversations", id: message.conversationid }),
fields: {
@@ -118,8 +135,12 @@ export const registerMessagingHandlers = ({ socket, client }) => {
};
const handleConversationChanged = (data) => {
if (!data) return;
const { conversationId, type, job_conversations, ...fields } = data;
logLocal("handleConversationChanged", data);
// Identify the conversation in the Apollo cache
const cacheId = client.cache.identify({
__typename: "conversations",
@@ -161,23 +182,60 @@ export const registerMessagingHandlers = ({ socket, client }) => {
};
const handleNewMessage = ({ conversationId, message }) => {
if (!conversationId || !message.id || !message.text) {
return;
}
logLocal("handleNewMessage", { conversationId, message });
client.cache.modify({
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
fields: {
messages(existing = []) {
// Ensure that the `message` object matches the schema
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 NewMessage on messages {
id
body
createdAt
selectedMedia
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 { Button } from "antd";
import React, { useState } from "react";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
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 { t } = useTranslation();
const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE);
const { socket } = useContext(SocketContext);
const handleToggleArchive = async () => {
setLoading(true);
await updateConversation({
variables: { id: conversation.id, archived: !conversation.archived },
refetchQueries: ["CONVERSATION_LIST_QUERY"]
const updatedConversation = await updateConversation({
variables: { id: conversation.id, archived: !conversation.archived }
});
if (socket) {
socket.emit("conversation-modified", {
conversationId: conversation.id,
bodyshopId: bodyshop.id,
archived: updatedConversation.data.update_conversations_by_pk.archived
});
}
setLoading(false);
};

View File

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

View File

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

View File

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

View File

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

View File

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