From 62dd3d7e8ec823f5285d7bd1fff329784cf70005 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 25 Nov 2024 11:38:59 -0800 Subject: [PATCH] feature/IO-3000-messaging-sockets-migration2 - Final fixes around sync / archive / receive Signed-off-by: Dave Richer --- client/src/redux/messaging/messaging.sagas.js | 20 +++-- server/graphql-client/queries.js | 31 ++++++++ server/sms/receive.js | 73 ++++++++++--------- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/client/src/redux/messaging/messaging.sagas.js b/client/src/redux/messaging/messaging.sagas.js index 7194ea4dc..fb519bcca 100644 --- a/client/src/redux/messaging/messaging.sagas.js +++ b/client/src/redux/messaging/messaging.sagas.js @@ -42,10 +42,13 @@ export function* openChatByPhone({ payload }) { } = yield client.query({ query: CONVERSATION_ID_BY_PHONE, variables: { phone: p.number }, - fetchPolicy: "network-only" + // THIS NEEDS TO REMAIN NO CACHE, IT CHECKS FOR NEW MESSAGES FOR SYNC + fetchPolicy: "no-cache" }); - if (conversations.length === 0) { + const existingConversation = conversations?.find((c) => c.phone_num === phone_num); + + if (!existingConversation) { // No conversation exists, create a new one const { data: { @@ -75,18 +78,17 @@ export function* openChatByPhone({ payload }) { // Set the newly created conversation as selected yield put(setSelectedConversation(createdConversation.id)); - } else if (conversations.length === 1) { - const conversation = conversations[0]; - let updatedConversation = conversation; + } else { + let updatedConversation = existingConversation; - if (conversation.archived) { + if (existingConversation.archived) { // Conversation is archived, unarchive it in the DB const { data: { update_conversations_by_pk: unarchivedConversation } } = yield client.mutate({ mutation: TOGGLE_CONVERSATION_ARCHIVE, variables: { - id: conversation.id, + id: existingConversation.id, archived: false } }); @@ -115,10 +117,6 @@ export function* openChatByPhone({ payload }) { } }); } - } else { - // Multiple conversations found - console.error("ERROR: Multiple conversations found."); - yield put(setSelectedConversation(null)); } } catch (error) { console.error("Error in openChatByPhone saga.", error); diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 4d82f0025..cf5e2b016 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2577,3 +2577,34 @@ exports.MARK_MESSAGES_AS_READ = `mutation MARK_MESSAGES_AS_READ($conversationId: } } `; + +exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conversations_insert_input!]!) { + insert_conversations(objects: $conversation) { + returning { + id + phone_num + archived + label + unreadcnt + job_conversations { + jobid + conversationid + job { + id + ro_number + ownr_fn + ownr_ln + ownr_co_nm + } + } + messages_aggregate(where: { read: { _eq: false }, isoutbound: { _eq: false } }) { + aggregate { + count + } + } + created_at + updated_at + } + } +} +`; diff --git a/server/sms/receive.js b/server/sms/receive.js index 63c11a260..fef921912 100644 --- a/server/sms/receive.js +++ b/server/sms/receive.js @@ -34,6 +34,7 @@ exports.receive = async (req, res) => { } try { + // Step 1: Find the bodyshop and existing conversation const response = await client.request(queries.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, { mssid: req.body.MessagingServiceSid, phone: phone(req.body.From).phoneNumber @@ -44,53 +45,51 @@ exports.receive = async (req, res) => { } const bodyshop = response.bodyshops[0]; - const isNewConversation = bodyshop.conversations.length === 0; - const isDuplicate = bodyshop.conversations.length > 1; + const existingConversation = bodyshop.conversations[0]; // Expect only one conversation per phone number per bodyshop + let conversationid; let newMessage = { msid: req.body.SmsMessageSid, text: req.body.Body, image: !!req.body.MediaUrl0, - image_path: generateMediaArray(req.body) + image_path: generateMediaArray(req.body), + isoutbound: false, + userid: null // Add additional fields as necessary }; - if (isDuplicate) { - logger.log("sms-inbound-error", "ERROR", "api", null, { - ...loggerData, - messagingServiceSid: req.body.MessagingServiceSid, - type: "duplicate-phone" - }); - return res.status(400).json({ success: false, error: "Duplicate phone number" }); - } + if (existingConversation) { + // Use the existing conversation + conversationid = existingConversation.id; - if (isNewConversation) { - newMessage.conversation = { - data: { + // Unarchive the conversation if necessary + if (existingConversation.archived) { + await client.request(queries.UNARCHIVE_CONVERSATION, { + id: conversationid, + archived: false + }); + } + } else { + // Create a new conversation + const newConversationResponse = await client.request(queries.CREATE_CONVERSATION, { + conversation: { bodyshopid: bodyshop.id, phone_num: phone(req.body.From).phoneNumber, archived: false } - }; - } else { - const existingConversation = bodyshop.conversations[0]; - - // Update the conversation to unarchive it - if (existingConversation.archived) { - await client.request(queries.UNARCHIVE_CONVERSATION, { - id: existingConversation.id, - archived: false - }); - } - - newMessage.conversationid = existingConversation.id; + }); + const createdConversation = newConversationResponse.insert_conversations.returning[0]; + conversationid = createdConversation.id; } - const query = isNewConversation ? queries.RECEIVE_MESSAGE : queries.INSERT_MESSAGE; - const variables = isNewConversation - ? { msg: newMessage } - : { msg: newMessage, conversationid: newMessage.conversationid }; + // Ensure `conversationid` is added to the message + newMessage.conversationid = conversationid; + + // Step 3: Insert the message into the conversation + const insertresp = await client.request(queries.INSERT_MESSAGE, { + msg: newMessage, + conversationid: conversationid + }); - const insertresp = await client.request(query, variables); const message = insertresp?.insert_messages?.returning?.[0]; const conversation = message?.conversation || null; @@ -98,6 +97,7 @@ exports.receive = async (req, res) => { throw new Error("Conversation data is missing from the response."); } + // Step 4: Notify clients through Redis const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id); const conversationRoom = getBodyshopConversationRoom({ bodyshopId: conversation.bodyshop.id, @@ -113,19 +113,20 @@ exports.receive = async (req, res) => { ioRedis.to(broadcastRoom).emit("new-message-summary", { ...commonPayload, - existingConversation: !isNewConversation, - newConversation: isNewConversation ? conversation : null, + existingConversation: !!existingConversation, + newConversation: !existingConversation ? conversation : null, summary: true }); ioRedis.to(conversationRoom).emit("new-message-detailed", { newMessage: message, ...commonPayload, - newConversation: isNewConversation ? conversation : null, - existingConversation: !isNewConversation, + newConversation: !existingConversation ? conversation : null, + existingConversation: !!existingConversation, summary: false }); + // Step 5: Send FCM notification const fcmresp = await admin.messaging().send({ topic: `${message.conversation.bodyshop.imexshopid}-messaging`, notification: {