diff --git a/client/package.json b/client/package.json
index 6f31ce130..24b997708 100644
--- a/client/package.json
+++ b/client/package.json
@@ -51,6 +51,7 @@
"react-i18next": "^12.2.0",
"react-icons": "^4.7.1",
"react-image-lightbox": "^5.1.4",
+ "react-intersection-observer": "^9.4.3",
"react-number-format": "^5.1.3",
"react-redux": "^8.0.5",
"react-resizable": "^3.0.4",
diff --git a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
index 816772291..a9d48871e 100644
--- a/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
+++ b/client/src/components/chat-conversation-list/chat-conversation-list.component.jsx
@@ -1,5 +1,5 @@
import { Badge, List, Tag } from "antd";
-import React from "react";
+import React, { useEffect } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
@@ -18,60 +18,73 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setSelectedConversation(conversationId)),
});
-export function ChatConversationListComponent({
- conversationList,
- selectedConversation,
- setSelectedConversation,
-}) {
- return (
-
-
(
- setSelectedConversation(item.id)}
- className={`chat-list-item ${
- item.id === selectedConversation
- ? "chat-list-selected-conversation"
- : null
- }`}
- >
-
- {item.label &&
{item.label}
}
- {item.job_conversations.length > 0 ? (
-
- {item.job_conversations.map((j, idx) => (
-
-
-
- ))}
-
- ) : (
-
{item.phone_num}
- )}
-
-
-
- {item.job_conversations.length > 0
- ? item.job_conversations.map((j, idx) => (
-
- {j.job.ro_number}
-
- ))
- : null}
+const ChatConversationListComponent = React.forwardRef(
+ function ChatConversationListComponent(
+ {
+ conversationList,
+ selectedConversation,
+ setSelectedConversation,
+ subscribeToMoreConversations,
+ },
+ ref
+ ) {
+ useEffect(
+ () => subscribeToMoreConversations(),
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ return (
+
+
(
+ setSelectedConversation(item.id)}
+ className={`chat-list-item ${
+ item.id === selectedConversation
+ ? "chat-list-selected-conversation"
+ : null
+ }`}
+ >
+
+ {item.label &&
{item.label}
}
+ {item.job_conversations.length > 0 ? (
+
+ {item.job_conversations.map((j, idx) => (
+
+
+
+ ))}
+
+ ) : (
+
{item.phone_num}
+ )}
- {item.updated_at}
-
-
-
- )}
- />
-
- );
-}
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(ChatConversationListComponent);
+
+
+ {item.job_conversations.length > 0
+ ? item.job_conversations.map((j, idx) => (
+
+ {j.job.ro_number}
+
+ ))
+ : null}
+
+
{item.updated_at}
+
+
+
+ )}
+ footer={
}
+ />
+
+ );
+ }
+);
+
+export default connect(mapStateToProps, mapDispatchToProps, null, {
+ forwardRef: true,
+})(ChatConversationListComponent);
diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx
index 9a4dc33eb..a3fda4843 100644
--- a/client/src/components/chat-popup/chat-popup.component.jsx
+++ b/client/src/components/chat-popup/chat-popup.component.jsx
@@ -4,7 +4,7 @@ import {
ShrinkOutlined,
SyncOutlined,
} from "@ant-design/icons";
-import { useQuery } from "@apollo/client";
+import { useQuery, useSubscription } from "@apollo/client";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -12,7 +12,8 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
CONVERSATION_LIST_QUERY,
- UNREAD_CONVERSATION_COUNT,
+ CONVERSATION_LIST_SUBSCRIPTION,
+ UNREAD_CONVERSATION_COUNT_SUBSCRIPTION,
} from "../../graphql/conversations.queries";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import {
@@ -24,6 +25,7 @@ import ChatConversationContainer from "../chat-conversation/chat-conversation.co
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import "./chat-popup.styles.scss";
+import { useInView } from "react-intersection-observer";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -40,19 +42,29 @@ export function ChatPopupComponent({
}) {
const { t } = useTranslation();
const [pollInterval, setpollInterval] = useState(0);
+ const { ref, inView } = useInView({});
- const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
- fetchPolicy: "network-only",
- nextFetchPolicy: "network-only",
- ...(pollInterval > 0 ? { pollInterval } : {}),
- });
+ const { data: unreadData } = useSubscription(
+ UNREAD_CONVERSATION_COUNT_SUBSCRIPTION
+ );
- const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
- fetchPolicy: "network-only",
- nextFetchPolicy: "network-only",
- skip: !chatVisible,
- ...(pollInterval > 0 ? { pollInterval } : {}),
- });
+ // const { data: unreadData, refetch: unreadRefetch } = useQuery(
+ // UNREAD_CONVERSATION_COUNT,
+ // {
+ // fetchPolicy: "network-only",
+ // nextFetchPolicy: "network-only",
+ // }
+ // );
+
+ const { loading, data, called, refetch, fetchMore, subscribeToMore } =
+ useQuery(CONVERSATION_LIST_QUERY, {
+ variables: {
+ offset: 0,
+ },
+ fetchPolicy: "cache-and-network",
+ nextFetchPolicy: "cache-first",
+ skip: !chatVisible,
+ });
const fcmToken = sessionStorage.getItem("fcmtoken");
@@ -68,12 +80,15 @@ export function ChatPopupComponent({
if (called && chatVisible) refetch();
}, [chatVisible, called, refetch]);
- // const unreadCount = data
- // ? data.conversations.reduce(
- // (acc, val) => val.messages_aggregate.aggregate.count + acc,
- // 0
- // )
- // : 0;
+ useEffect(() => {
+ if (inView && data && data.conversations) {
+ fetchMore({
+ variables: {
+ offset: data.conversations.length,
+ },
+ });
+ }
+ }, [inView, data, fetchMore]);
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
@@ -110,6 +125,26 @@ export function ChatPopupComponent({
) : (
{
+ subscribeToMore({
+ document: CONVERSATION_LIST_SUBSCRIPTION,
+ variables: { offset: 0 },
+ updateQuery: (prev, { subscriptionData }) => {
+ if (
+ !subscriptionData.data ||
+ subscriptionData.data.conversations.length === 0
+ )
+ return prev;
+ const newConversations =
+ subscriptionData.data.conversations;
+
+ return Object.assign({}, prev, {
+ conversations: [...newConversations],
+ });
+ },
+ });
+ }}
+ ref={ref}
/>
)}
diff --git a/client/src/graphql/conversations.queries.js b/client/src/graphql/conversations.queries.js
index ef73f546b..53de38b62 100644
--- a/client/src/graphql/conversations.queries.js
+++ b/client/src/graphql/conversations.queries.js
@@ -1,35 +1,36 @@
import { gql } from "@apollo/client";
-// export const CONVERSATION_LIST_SUBSCRIPTION = gql`
-// subscription CONVERSATION_LIST_SUBSCRIPTION {
-// conversations(
-// order_by: { updated_at: desc }
-// limit: 50
-// where: { archived: { _eq: false } }
-// ) {
-// phone_num
-// id
-// updated_at
-// unreadcnt
-// messages_aggregate(
-// where: { read: { _eq: false }, isoutbound: { _eq: false } }
-// ) {
-// aggregate {
-// count
-// }
-// }
-// job_conversations {
-// job {
-// id
-// ro_number
-// ownr_fn
-// ownr_ln
-// ownr_co_nm
-// }
-// }
-// }
-// }
-// `;
+export const CONVERSATION_LIST_SUBSCRIPTION = gql`
+ subscription CONVERSATION_LIST_SUBSCRIPTION($offset: Int!) {
+ conversations(
+ order_by: { updated_at: desc }
+ limit: 1
+ offset: $offset
+ where: { archived: { _eq: false } }
+ ) {
+ phone_num
+ id
+ updated_at
+ unreadcnt
+ messages_aggregate(
+ where: { read: { _eq: false }, isoutbound: { _eq: false } }
+ ) {
+ aggregate {
+ count
+ }
+ }
+ job_conversations {
+ job {
+ id
+ ro_number
+ ownr_fn
+ ownr_ln
+ ownr_co_nm
+ }
+ }
+ }
+ }
+`;
export const UNREAD_CONVERSATION_COUNT = gql`
query UNREAD_CONVERSATION_COUNT {
@@ -43,11 +44,24 @@ export const UNREAD_CONVERSATION_COUNT = gql`
}
`;
+export const UNREAD_CONVERSATION_COUNT_SUBSCRIPTION = gql`
+ subscription UNREAD_CONVERSATION_COUNT_SUBSCRIPTION {
+ messages_aggregate(
+ where: { read: { _eq: false }, isoutbound: { _eq: false } }
+ ) {
+ aggregate {
+ count
+ }
+ }
+ }
+`;
+
export const CONVERSATION_LIST_QUERY = gql`
- query CONVERSATION_LIST_QUERY {
+ query CONVERSATION_LIST_QUERY($offset: Int!) {
conversations(
order_by: { updated_at: desc }
- limit: 50
+ limit: 20
+ offset: $offset
where: { archived: { _eq: false } }
) {
phone_num
diff --git a/client/src/utils/GraphQLClient.js b/client/src/utils/GraphQLClient.js
index 1861b8b7b..b6c2c0091 100644
--- a/client/src/utils/GraphQLClient.js
+++ b/client/src/utils/GraphQLClient.js
@@ -3,7 +3,10 @@ import { setContext } from "@apollo/client/link/context";
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
import { RetryLink } from "@apollo/client/link/retry";
import { WebSocketLink } from "@apollo/client/link/ws";
-import { getMainDefinition } from "@apollo/client/utilities";
+import {
+ getMainDefinition,
+ offsetLimitPagination,
+} from "@apollo/client/utilities";
//import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger";
//import axios from "axios";
@@ -140,7 +143,15 @@ middlewares.push(
)
);
-const cache = new InMemoryCache({});
+const cache = new InMemoryCache({
+ typePolicies: {
+ Query: {
+ fields: {
+ conversations: offsetLimitPagination(),
+ },
+ },
+ },
+});
const client = new ApolloClient({
link: ApolloLink.from(middlewares),
diff --git a/client/yarn.lock b/client/yarn.lock
index 04749e444..d972ced66 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -11168,6 +11168,11 @@ react-image-lightbox@^5.1.4:
prop-types "^15.7.2"
react-modal "^3.11.1"
+react-intersection-observer@^9.4.3:
+ version "9.4.3"
+ resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.4.3.tgz#ec84ce0c25cad548075130ea045ac5c7adf908f3"
+ integrity sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==
+
react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"