feature/IO-3000-messaging-sockets-migration2 - Handle some of the PR notes

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-11-25 08:36:33 -08:00
parent 49044e5669
commit cbc8665636
8 changed files with 109 additions and 45 deletions

View File

@@ -4,8 +4,17 @@ 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";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatArchiveButton({ conversation, bodyshop }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});
export function ChatArchiveButton({ conversation, bodyshop }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE);
@@ -36,3 +45,5 @@ export default function ChatArchiveButton({ conversation, bodyshop }) {
</Button>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatArchiveButton);

View File

@@ -64,7 +64,7 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
);
};
// TODO: Can go back into virtuoso for additional fetch
// CAN DO: Can go back into virtuoso for additional fetch
// endReached={loadMoreConversations} // Calls loadMoreConversations when scrolled to the bottom
return (

View File

@@ -6,8 +6,17 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { REMOVE_CONVERSATION_TAG } from "../../graphql/job-conversations.queries";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatConversationTitleTags({ jobConversations, bodyshop }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});
export function ChatConversationTitleTags({ jobConversations, bodyshop }) {
const [removeJobConversation] = useMutation(REMOVE_CONVERSATION_TAG);
const { socket } = useContext(SocketContext);
@@ -66,3 +75,5 @@ export default function ChatConversationTitleTags({ jobConversations, bodyshop }
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationTitleTags);

View File

@@ -6,19 +6,27 @@ import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conv
import ChatLabelComponent from "../chat-label/chat-label.component";
import ChatPrintButton from "../chat-print-button/chat-print-button.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatConversationTitle({ conversation, bodyshop }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});
export function ChatConversationTitle({ conversation }) {
return (
<Space wrap>
<PhoneNumberFormatter>{conversation && conversation.phone_num}</PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} bodyshop={bodyshop} />
<ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags
jobConversations={(conversation && conversation.job_conversations) || []}
bodyshop={bodyshop}
/>
<ChatTagRoContainer conversation={conversation || []} bodyshop={bodyshop} />
<ChatArchiveButton conversation={conversation} bodyshop={bodyshop} />
<ChatConversationTitleTags jobConversations={(conversation && conversation.job_conversations) || []} />
<ChatTagRoContainer conversation={conversation || []} />
<ChatArchiveButton conversation={conversation} />
</Space>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationTitle);

View File

@@ -5,8 +5,17 @@ import ChatMessageListComponent from "../chat-messages-list/chat-message-list.co
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
import "./chat-conversation.styles.scss";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatConversationComponent({
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});
export function ChatConversationComponent({
subState,
conversation,
messages,
@@ -31,3 +40,5 @@ export default function ChatConversationComponent({
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationComponent);

View File

@@ -1,6 +1,6 @@
import { useApolloClient, useQuery } from "@apollo/client";
import axios from "axios";
import React, { useContext, useEffect, useState } from "react";
import React, { useCallback, useContext, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import SocketContext from "../../contexts/SocketIO/socketContext";
@@ -14,8 +14,6 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
export default connect(mapStateToProps, null)(ChatConversationContainer);
export function ChatConversationContainer({ bodyshop, selectedConversation }) {
const client = useApolloClient();
const { socket } = useContext(SocketContext);
@@ -30,38 +28,40 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
fetchPolicy: "network-only"
});
// Utility to update Apollo cache
const updateCacheWithReadMessages = (conversationId, messageIds) => {
if (!conversationId || !messageIds || messageIds.length === 0) return;
const updateCacheWithReadMessages = useCallback(
(conversationId, messageIds) => {
if (!conversationId || !messageIds || messageIds.length === 0) return;
messageIds.forEach((messageId) => {
// Mark individual messages as read
messageIds.forEach((messageId) => {
client.cache.modify({
id: client.cache.identify({ __typename: "messages", id: messageId }),
fields: {
read() {
return true; // Mark message as read
}
}
});
});
// Update aggregate unread count for the conversation
client.cache.modify({
id: client.cache.identify({ __typename: "messages", id: messageId }),
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
fields: {
read() {
return true; // Mark message as read
messages_aggregate(existingAggregate) {
return {
...existingAggregate,
aggregate: {
...existingAggregate.aggregate,
count: 0 // No unread messages remaining
}
};
}
}
});
});
// Optionally update aggregate unread count
client.cache.modify({
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
fields: {
messages_aggregate(existingAggregate) {
const updatedAggregate = {
...existingAggregate,
aggregate: {
...existingAggregate.aggregate,
count: 0 // No unread messages remaining
}
};
return updatedAggregate;
}
}
});
};
},
[client.cache]
);
// Handle WebSocket events
useEffect(() => {
@@ -80,7 +80,7 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
return () => {
socket.off("conversation-changed", handleConversationChange);
};
}, [socket, client]);
}, [socket, client, updateCacheWithReadMessages]);
// Handle joining/leaving conversation
useEffect(() => {
@@ -143,7 +143,8 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
conversation={convoData ? convoData.conversations_by_pk : {}}
messages={convoData ? convoData.conversations_by_pk.messages : []}
handleMarkConversationAsRead={handleMarkConversationAsRead}
bodyshop={bodyshop}
/>
);
}
export default connect(mapStateToProps, null)(ChatConversationContainer);

View File

@@ -5,8 +5,17 @@ import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_CONVERSATION_LABEL } from "../../graphql/conversations.queries";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatLabel({ conversation, bodyshop }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
export function ChatLabel({ conversation, bodyshop }) {
const [loading, setLoading] = useState(false);
const [editing, setEditing] = useState(false);
const [value, setValue] = useState(conversation.label);
@@ -67,3 +76,5 @@ export default function ChatLabel({ conversation, bodyshop }) {
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatLabel);

View File

@@ -9,8 +9,17 @@ import { INSERT_CONVERSATION_TAG } from "../../graphql/job-conversations.queries
import { SEARCH_FOR_JOBS } from "../../graphql/jobs.queries";
import ChatTagRo from "./chat-tag-ro.component";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors.js";
import { connect } from "react-redux";
export default function ChatTagRoContainer({ conversation, bodyshop }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = () => ({});
export function ChatTagRoContainer({ conversation, bodyshop }) {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const { socket } = useContext(SocketContext);
@@ -86,3 +95,5 @@ export default function ChatTagRoContainer({ conversation, bodyshop }) {
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatTagRoContainer);