feature/IO-3000-Migrate-MSG-to-Sockets - Progress Checkpoint

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-11-18 21:06:56 -08:00
parent 6e6c44f2b9
commit 03ae7bb160
6 changed files with 64 additions and 30 deletions

View File

@@ -4,15 +4,19 @@ import { createStructuredSelector } from "reselect";
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";
import axios from "axios";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx"; import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { updateUnreadCount } from "../../redux/messaging/messaging.actions.js";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation, selectedConversation: selectSelectedConversation,
bodyshop: selectBodyshop bodyshop: selectBodyshop
}); });
export function ChatConversationContainer({ bodyshop, selectedConversation }) { const mapDispatchToProps = (dispatch) => ({
updateUnreadCounts: (data) => dispatch(updateUnreadCount(data.conversationId, data.unreadcnt))
});
export function ChatConversationContainer({ bodyshop, selectedConversation, updateUnreadCounts }) {
const { socket } = useContext(SocketContext); const { socket } = useContext(SocketContext);
const [conversationDetails, setConversationDetails] = useState({}); const [conversationDetails, setConversationDetails] = useState({});
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
@@ -36,26 +40,34 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
setMessages((prevMessages) => [...prevMessages, message]); setMessages((prevMessages) => [...prevMessages, message]);
}); });
socket.on("unread-count-updated", (data) => {
updateUnreadCounts(data);
});
socket.on("conversation-list-updated", (data) => {
setConversationDetails(data.conversation);
setMessages(data.messages);
});
return () => { return () => {
socket.emit("leave-conversation", selectedConversation); socket.emit("leave-conversation", selectedConversation);
socket.off("conversation-details"); socket.off("conversation-details");
socket.off("new-message"); socket.off("new-message");
socket.off("unread-count-updated");
socket.off("conversation-list-updated");
}; };
} }
}, [socket, selectedConversation]); }, [socket, selectedConversation, updateUnreadCounts]);
// Mark messages as read // Mark messages as read
const handleMarkConversationAsRead = async () => { const handleMarkConversationAsRead = async () => {
if (messages.some((msg) => !msg.read) && !markingAsReadInProgress) { if (messages.some((msg) => !msg.read) && !markingAsReadInProgress) {
setMarkingAsReadInProgress(true); setMarkingAsReadInProgress(true);
// Emit a WebSocket event to mark messages as read // Emit a WebSocket event to mark messages as read
socket.emit("mark-as-read", { conversationId: selectedConversation }); socket.emit("mark-as-read", {
conversationId: selectedConversation,
// Fallback to an API call to update the read status in the database imexshopid: bodyshop.imexshopid,
await axios.post("/sms/markConversationRead", { bodyshopId: bodyshop.id
conversationid: selectedConversation,
imexshopid: bodyshop.imexshopid
}); });
setMarkingAsReadInProgress(false); setMarkingAsReadInProgress(false);
@@ -76,4 +88,4 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
); );
} }
export default connect(mapStateToProps, null)(ChatConversationContainer); export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationContainer);

View File

@@ -81,13 +81,13 @@ const useSocket = (bodyshop) => {
dispatch({ type: "ADD_MESSAGE", payload: data.message }); dispatch({ type: "ADD_MESSAGE", payload: data.message });
}; };
const handleReadUpdated = ({ conversationId }) => { // const handleReadUpdated = ({ conversationId }) => {
dispatch({ type: "UPDATE_UNREAD_COUNT", payload: conversationId }); // dispatch({ type: "UPDATE_UNREAD_COUNT", payload: conversationId });
}; // };
socketInstance.on("messaging-list", handleMessagingList); socketInstance.on("messaging-list", handleMessagingList);
socketInstance.on("new-message", handleNewMessage); socketInstance.on("new-message", handleNewMessage);
socketInstance.on("read-updated", handleReadUpdated); // socketInstance.on("mark-as-read", handleReadUpdated);
socketInstance.on("connect", handleConnect); socketInstance.on("connect", handleConnect);
socketInstance.on("reconnect", handleReconnect); socketInstance.on("reconnect", handleReconnect);
socketInstance.on("connect_error", handleConnectionError); socketInstance.on("connect_error", handleConnectionError);

View File

@@ -145,7 +145,7 @@ export function Manage({ conflict, bodyshop, alerts, setAlerts }) {
}; };
fetchAlerts(); fetchAlerts();
}, []); }, [setAlerts]);
// Use useEffect to watch for new alerts // Use useEffect to watch for new alerts
useEffect(() => { useEffect(() => {

View File

@@ -52,12 +52,12 @@ export const addMessage = (message) => ({
// Add a Conversation to the list of conversations // Add a Conversation to the list of conversations
export const addConversation = (conversation) => ({ export const addConversation = (conversation) => ({
type: "ADD_CONVERSATION", type: MessagingActionTypes.ADD_CONVERSATION,
payload: conversation payload: conversation
}); });
// Update unread count for a conversation (e.g., after marking messages as read) // Update unread count for a conversation (e.g., after marking messages as read)
export const updateUnreadCount = (conversationId) => ({ export const updateUnreadCount = (conversationId, unreadCount) => ({
type: MessagingActionTypes.UPDATE_UNREAD_COUNT, type: MessagingActionTypes.UPDATE_UNREAD_COUNT,
payload: conversationId payload: { conversationId, unreadCount }
}); });

View File

@@ -8,7 +8,7 @@ const INITIAL_STATE = {
message: null, message: null,
conversations: [], // Holds the list of conversations conversations: [], // Holds the list of conversations
messages: [], // Holds the list of messages for the selected conversation messages: [], // Holds the list of messages for the selected conversation
unreadCount: 0, unreadcnt: 0,
searchingForConversation: false searchingForConversation: false
}; };
@@ -83,12 +83,21 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
conversations: { conversations: {
...state.conversations, ...state.conversations,
conversations: state.conversations.conversations.map((conversation) => conversations: state.conversations.conversations.map((conversation) =>
conversation.id === action.payload conversation.id === action.payload.conversationId
? { ...conversation, unreadcnt: 0 } // Reset unread count for the selected conversation ? { ...conversation, unreadcnt: action.payload.unreadcnt } // Update unread count to the value in the payload
: conversation : conversation
) )
}, },
unreadCount: Math.max(state.unreadCount - 1, 0) // Ensure unreadCount does not go below zero unreadcnt: Math.max(
state.conversations.conversations.reduce(
(total, conversation) =>
conversation.id === action.payload.conversationId
? total + action.payload.unreadcnt
: total + conversation.unreadcnt,
0
),
0
) // Recalculate the global unreadcnt based on all conversations
}; };
default: default:

View File

@@ -1,6 +1,5 @@
const { admin } = require("../firebase/firebase-handler"); const { admin } = require("../firebase/firebase-handler");
const { MARK_MESSAGES_AS_READ, GET_CONVERSATIONS, GET_CONVERSATION_DETAILS } = require("../graphql-client/queries"); const { MARK_MESSAGES_AS_READ, GET_CONVERSATIONS, GET_CONVERSATION_DETAILS } = require("../graphql-client/queries");
const logger = require("../utils/logger");
const { phone } = require("phone"); const { phone } = require("phone");
const { client: gqlClient } = require("../graphql-client/graphql-client"); const { client: gqlClient } = require("../graphql-client/graphql-client");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
@@ -151,7 +150,6 @@ const redisSocketEvents = ({
const conversations = await client.request(GET_CONVERSATIONS, { bodyshopId: bodyshopUUID }); const conversations = await client.request(GET_CONVERSATIONS, { bodyshopId: bodyshopUUID });
socket.emit("messaging-list", { conversations }); socket.emit("messaging-list", { conversations });
} catch (error) { } catch (error) {
console.dir(error);
logger.log("error", "Failed to fetch conversations", error); logger.log("error", "Failed to fetch conversations", error);
socket.emit("error", { message: "Failed to fetch conversations" }); socket.emit("error", { message: "Failed to fetch conversations" });
} }
@@ -171,19 +169,35 @@ const redisSocketEvents = ({
} }
}; };
const markAsRead = async ({ conversationId, userId }) => { const markAsRead = async ({ conversationId, userId, imexshopid, bodyshopId }) => {
try { try {
await client.request(MARK_MESSAGES_AS_READ, { conversationId, userId }); await client.request(MARK_MESSAGES_AS_READ, { conversationId, userId });
io.to(`conversation-${conversationId}`).emit("read-updated", { conversationId });
// Fetch the updated unread count for this conversation
const conversations = await client.request(GET_CONVERSATIONS, { bodyshopId });
// Emit the updated unread count to all clients
const room = `conversation-${conversationId}`;
io.to(room).emit("messaging-list", { conversations });
admin.messaging().send({
topic: `${imexshopid}-messaging`,
data: {
type: "messaging-mark-conversation-read",
conversationid: conversationId || ""
}
});
} catch (error) { } catch (error) {
logger.log("error", "Failed to mark messages as read", error); logger.log("Failed to mark messages as read", "error", null, null, {
message: error.message,
stack: error.stack
});
socket.emit("error", { message: "Failed to mark messages as read" }); socket.emit("error", { message: "Failed to mark messages as read" });
} }
}; };
const sendMessage = (data) => { const sendMessage = (data) => {
const { to, messagingServiceSid, body, conversationid, selectedMedia, imexshopid, user } = data; const { to, messagingServiceSid, body, conversationid, selectedMedia, imexshopid, user } = data;
console.dir({ data });
logger.log("sms-outbound", "DEBUG", user.email, null, { logger.log("sms-outbound", "DEBUG", user.email, null, {
messagingServiceSid: messagingServiceSid, messagingServiceSid: messagingServiceSid,
to: phone(to).phoneNumber, to: phone(to).phoneNumber,
@@ -217,7 +231,6 @@ const redisSocketEvents = ({
gqlClient gqlClient
.request(queries.INSERT_MESSAGE, { msg: newMessage, conversationid }) .request(queries.INSERT_MESSAGE, { msg: newMessage, conversationid })
.then((r2) => { .then((r2) => {
//console.log("Responding GQL Message ID", JSON.stringify(r2));
logger.log("sms-outbound-success", "DEBUG", user.email, null, { logger.log("sms-outbound-success", "DEBUG", user.email, null, {
msid: message.sid, msid: message.sid,
conversationid conversationid