151 lines
5.4 KiB
JavaScript
151 lines
5.4 KiB
JavaScript
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
|
|
import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client";
|
|
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
|
import { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { CONVERSATION_LIST_QUERY, UNREAD_CONVERSATION_COUNT } from "../../graphql/conversations.queries";
|
|
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
|
import { selectChatVisible, selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
|
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
|
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
|
|
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
|
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
|
|
import "./chat-popup.styles.scss";
|
|
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
selectedConversation: selectSelectedConversation,
|
|
chatVisible: selectChatVisible
|
|
});
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
toggleChatVisible: () => dispatch(toggleChatVisible())
|
|
});
|
|
|
|
export function ChatPopupComponent({ chatVisible, selectedConversation, toggleChatVisible }) {
|
|
const { t } = useTranslation();
|
|
const [pollInterval, setPollInterval] = useState(0);
|
|
const { socket } = useSocket();
|
|
const client = useApolloClient(); // Apollo Client instance for cache operations
|
|
|
|
// Lazy query for conversations
|
|
const [getConversations, { loading, data, refetch }] = useLazyQuery(CONVERSATION_LIST_QUERY, {
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only",
|
|
skip: !chatVisible,
|
|
...(pollInterval > 0 ? { pollInterval } : {})
|
|
});
|
|
|
|
// Query for unread count when chat is not visible
|
|
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only",
|
|
pollInterval: 60 * 1000 // TODO: This is a fix for now, should be coming from sockets
|
|
});
|
|
|
|
// Socket connection status
|
|
useEffect(() => {
|
|
const handleSocketStatus = () => {
|
|
if (socket?.connected) {
|
|
setPollInterval(15 * 60 * 1000); // 15 minutes
|
|
} else {
|
|
setPollInterval(60 * 1000); // 60 seconds
|
|
}
|
|
};
|
|
|
|
handleSocketStatus();
|
|
|
|
if (socket) {
|
|
socket.on("connect", handleSocketStatus);
|
|
socket.on("disconnect", handleSocketStatus);
|
|
}
|
|
|
|
return () => {
|
|
if (socket) {
|
|
socket.off("connect", handleSocketStatus);
|
|
socket.off("disconnect", handleSocketStatus);
|
|
}
|
|
};
|
|
}, [socket]);
|
|
|
|
// Fetch conversations when chat becomes visible
|
|
useEffect(() => {
|
|
if (chatVisible)
|
|
getConversations({
|
|
variables: {
|
|
offset: 0
|
|
}
|
|
}).catch((err) => {
|
|
console.error(`Error fetching conversations: ${(err, err.message || "")}`);
|
|
});
|
|
}, [chatVisible, getConversations]);
|
|
|
|
// Get unread count from the cache
|
|
const unreadCount = (() => {
|
|
try {
|
|
const cachedData = client.readQuery({
|
|
query: CONVERSATION_LIST_QUERY,
|
|
variables: { offset: 0 }
|
|
});
|
|
|
|
if (!cachedData?.conversations) {
|
|
return unreadData?.messages_aggregate?.aggregate?.count;
|
|
}
|
|
|
|
// Aggregate unread message count
|
|
return cachedData.conversations.reduce((total, conversation) => {
|
|
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
|
|
return total + unread;
|
|
}, 0);
|
|
} catch (error) {
|
|
console.warn("Unread count not found in cache:", error);
|
|
return 0; // Fallback if not in cache
|
|
}
|
|
})();
|
|
|
|
return (
|
|
<Badge count={unreadCount}>
|
|
<Card size="small">
|
|
{chatVisible ? (
|
|
<div className="chat-popup">
|
|
<Space align="center">
|
|
<Typography.Title level={4}>{t("messaging.labels.messaging")}</Typography.Title>
|
|
<ChatNewConversation />
|
|
<Tooltip title={t("messaging.labels.recentonly")}>
|
|
<InfoCircleOutlined />
|
|
</Tooltip>
|
|
<SyncOutlined style={{ cursor: "pointer" }} onClick={() => refetch()} />
|
|
{!socket?.connected && <Tag color="yellow">{t("messaging.labels.nopush")}</Tag>}
|
|
</Space>
|
|
<ShrinkOutlined
|
|
onClick={() => toggleChatVisible()}
|
|
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
|
|
/>
|
|
|
|
<Row gutter={[8, 8]} className="chat-popup-content">
|
|
<Col span={8}>
|
|
{loading ? (
|
|
<LoadingSpinner />
|
|
) : (
|
|
<ChatConversationListComponent conversationList={data ? data.conversations : []} />
|
|
)}
|
|
</Col>
|
|
<Col span={16}>{selectedConversation ? <ChatConversationContainer /> : null}</Col>
|
|
</Row>
|
|
</div>
|
|
) : (
|
|
<div onClick={() => toggleChatVisible()} style={{ cursor: "pointer" }}>
|
|
<MessageOutlined className="chat-popup-info-icon" />
|
|
<strong>{t("messaging.labels.messaging")}</strong>
|
|
</div>
|
|
)}
|
|
</Card>
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);
|