@pubuduth-aplicy/chat-ui 2.1.35 → 2.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubuduth-aplicy/chat-ui",
3
- "version": "2.1.35",
3
+ "version": "2.1.37",
4
4
  "description": "This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -16,6 +16,7 @@
16
16
  "axios": "^1.8.2",
17
17
  "react": "^19.0.0",
18
18
  "react-dom": "^19.0.0",
19
+ "react-intersection-observer": "^9.16.0",
19
20
  "socket.io-client": "^4.8.1",
20
21
  "zustand": "^5.0.3"
21
22
  },
@@ -1,11 +1,29 @@
1
+ import { useEffect } from 'react';
1
2
  import useChatUIStore from '../stores/Zustant';
2
3
  import MessageContainer from './messages/MessageContainer';
3
4
  import { Sidebar } from './sidebar/Sidebar'
5
+ import { useChatContext } from '../providers/ChatProvider';
6
+
4
7
  // import MessageContainer from './components/messages/MessageContainer'
5
8
  // import useConversation from '../../zustand/useConversation';
6
9
 
7
10
  export const Chat = () => {
8
- const { selectedConversation } = useChatUIStore();
11
+ const { selectedConversation, messages, setMessages, updateMessageStatus } = useChatUIStore();
12
+ const {socket}=useChatContext()
13
+ useEffect(() => {
14
+ socket.on("receiveMessage", (messageData) => {
15
+ setMessages([...messages, { ...messageData, status: "sent" }]);
16
+ });
17
+
18
+ socket.on("messageDelivered", ({ messageId }) => {
19
+ updateMessageStatus(messageId, "delivered");
20
+ });
21
+
22
+ return () => {
23
+ socket.off("receiveMessage");
24
+ socket.off("messageDelivered");
25
+ };
26
+ }, [messages, setMessages, updateMessageStatus]);
9
27
  return (
10
28
  <>
11
29
  <div className='container mx-auto mb-5'>
@@ -79,28 +79,6 @@ const MessageInput = () => {
79
79
  setIsSending(true);
80
80
  try {
81
81
  console.log("📤 Sending message:", message);
82
-
83
- // if (selectedConversation?._id) {
84
- // const response = await sendMessage({
85
- // chatId: selectedConversation.participantDetails._id,
86
- // senderId: userId,
87
- // message,
88
- // });
89
-
90
- // // You can log or handle the response here
91
- // console.log('Response from sendMessage:', response);
92
-
93
-
94
- // socket.emit("sendMessage", {
95
- // chatId: selectedConversation._id,
96
- // message,
97
- // senderId: userId,
98
- // receiverId: selectedConversation.participantDetails._id,
99
- // });
100
- // }
101
-
102
-
103
-
104
82
  mutation.mutate({
105
83
  chatId: selectedConversation?.participantDetails._id,
106
84
  senderId: userId,
@@ -1,103 +1,130 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { useEffect, useRef } from "react";
3
- // import useGetMessages from "../../hooks/useGetMessages";
4
- // import MessageSkeleton from "../skeletons/MessageSkeleton";
2
+ import { useCallback, useEffect, useRef } from "react";
5
3
  import Message from "./Message";
6
4
  import { useChatContext } from "../../providers/ChatProvider";
7
5
  import { useMessages } from "../../hooks/queries/useChatApi";
8
6
  import useChatUIStore from "../../stores/Zustant";
9
- // import useListenMessages from "../../hooks/useListenMessages";
7
+ import { useInfiniteQuery } from "@tanstack/react-query";
8
+ import { fetchMessages } from "../../service/messageService";
9
+ import { useInView } from "react-intersection-observer";
10
10
 
11
11
  const Messages = () => {
12
- const { selectedConversation,setMessages,messages } = useChatUIStore()
13
- const {userId,socket}=useChatContext()
14
- const { data, isLoading, isError, error } = useMessages(selectedConversation?._id, userId);
12
+ const { selectedConversation, setMessages, messages } = useChatUIStore();
13
+ const { userId, socket } = useChatContext();
14
+ const { ref, inView } = useInView();
15
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
15
16
 
16
- const lastMessageRef = useRef<HTMLDivElement>(null);
17
+ // const { data, isLoading, isError, error } = useMessages(selectedConversation?._id, userId);
17
18
 
19
+ const lastMessageRef = useRef<HTMLDivElement>(null);
18
20
 
19
- useEffect(() => {
20
- if (data) {
21
- setMessages(data.messages);
21
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
22
+ useInfiniteQuery({
23
+ queryKey: ["messages", selectedConversation?._id, userId],
24
+ queryFn: ({ pageParam = 1 }) =>
25
+ fetchMessages(selectedConversation?._id, userId, pageParam),
26
+ initialPageParam: 1,
27
+ getNextPageParam: (lastPage, allPages) => {
28
+ return lastPage.length ? allPages.length + 1 : undefined;
29
+ },
30
+ });
31
+
32
+ useEffect(() => {
33
+ if (inView) {
34
+ fetchNextPage();
22
35
  }
23
- }, [selectedConversation?._id,data]);
24
-
25
- // Listen for new messages from the server
26
- useEffect(() => {
27
- if (!socket || !selectedConversation?._id) return;
28
-
29
- const handleNewMessage = (newMessage:any) => {
30
- newMessage.shouldShake = true;
31
- console.log("📩 New message received:", newMessage);
32
- // setMessages([...messages, newMessage[1]]);
33
- setMessages((prevMessages) => [...prevMessages, newMessage]);
34
- };
35
-
36
- const handleStatusUpdate = ({ messageId, status }) => {
37
- setMessages(prev => prev.map(msg =>
38
- msg._id === messageId ? { ...msg, status } : msg
39
- ));
40
- };
41
-
42
- socket.on("newMessage", handleNewMessage);
43
- socket.on("messageStatusUpdated", handleStatusUpdate);
44
-
45
- return () => {
46
- socket.off("newMessage", handleNewMessage);
47
- socket.off("messageStatusUpdated", handleStatusUpdate);
48
- };
49
- }, [socket,setMessages, messages]);
50
-
51
- useEffect(() => {
52
- setTimeout(() => {
53
- lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
54
- }, 100);
55
- }, [ messages]);
56
-
57
- useEffect(() => {
58
- if (!socket || !messages.length) return;
59
-
60
- const observer = new IntersectionObserver((entries) => {
61
- entries.forEach(entry => {
62
- if (entry.isIntersecting) {
63
- const messageId = entry.target.getAttribute('data-message-id');
64
- if (messageId) {
65
- socket.emit('confirmDelivery', { messageId });
66
- }
67
- }
68
- });
69
- }, { threshold: 0.5 });
70
-
71
- const messageElements = document.querySelectorAll('[data-message-id]');
72
- messageElements.forEach(el => observer.observe(el));
73
-
74
- return () => observer.disconnect();
75
- }, [messages, socket]);
76
-
77
- if (isLoading) {
78
- return <p>Loading messages...</p>;
79
- }
80
-
81
- if (isError) {
82
- return <p>Error: {error?.message}</p>;
83
- }
84
-
85
- console.log("📩 Messages:", messages);
86
- console.log("📩 Messages Length:", messages?.length);
87
-
88
- return (
89
- // <div className="chatMessages">
90
- // {messages?.length > 0 ? (
91
- // messages?.map((message: any) => (
92
- // <div key={message._id} ref={lastMessageRef}>
93
- // <Message message={message} />
94
- // </div>
95
- // ))
96
- // ) : (
97
- // <p style={{ textAlign: "center" }}>Send a message to start the conversation</p>
98
- // )}
99
- // </div>
100
- <div className="chatMessages">
36
+ }, [fetchNextPage, inView]);
37
+
38
+ useEffect(() => {
39
+ if (data) {
40
+ console.log(data,'message data');
41
+
42
+ setMessages(data.messages);
43
+ }
44
+ }, [selectedConversation?._id, data]);
45
+
46
+ // Listen for new messages from the server
47
+ useEffect(() => {
48
+ if (!socket || !selectedConversation?._id) return;
49
+
50
+ const handleNewMessage = (newMessage: any) => {
51
+ newMessage.shouldShake = true;
52
+ console.log("📩 New message received:", newMessage);
53
+ setMessages((prevMessages) => [...prevMessages, newMessage[1]]);
54
+ };
55
+
56
+ const handleStatusUpdate = ({
57
+ messageId,
58
+ status,
59
+ }: {
60
+ messageId: string;
61
+ status: string;
62
+ }) => {
63
+ setMessages((prev) =>
64
+ prev.map((msg) => (msg.id === messageId ? { ...msg, status } : msg))
65
+ );
66
+ };
67
+
68
+ socket.on("newMessage", handleNewMessage);
69
+ socket.on("messageStatusUpdated", handleStatusUpdate);
70
+
71
+ return () => {
72
+ socket.off("newMessage", handleNewMessage);
73
+ socket.off("messageStatusUpdated", handleStatusUpdate);
74
+ };
75
+ }, [socket, setMessages, messages]);
76
+
77
+ useEffect(() => {
78
+ setTimeout(() => {
79
+ lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
80
+ }, 100);
81
+ }, [messages]);
82
+
83
+ useEffect(() => {
84
+ if (!socket || !messages.length) return;
85
+
86
+ const observer = new IntersectionObserver(
87
+ (entries) => {
88
+ entries.forEach((entry) => {
89
+ if (entry.isIntersecting) {
90
+ const messageId = entry.target.getAttribute("data-message-id");
91
+ if (messageId) {
92
+ socket.emit("confirmDelivery", { messageId });
93
+ }
94
+ }
95
+ });
96
+ },
97
+ { threshold: 0.5 }
98
+ );
99
+
100
+ const messageElements = document.querySelectorAll("[data-message-id]");
101
+ messageElements.forEach((el) => observer.observe(el));
102
+
103
+ return () => observer.disconnect();
104
+ }, [messages, socket]);
105
+
106
+ const handleScroll = useCallback(() => {
107
+ if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
108
+
109
+ const { scrollTop } = scrollContainerRef.current;
110
+
111
+ if (scrollTop < 100) {
112
+ fetchNextPage();
113
+ }
114
+ }, [fetchNextPage, isFetchingNextPage, hasNextPage]);
115
+
116
+ useEffect(() => {
117
+ const scrollContainer = scrollContainerRef.current;
118
+ if (scrollContainer) {
119
+ scrollContainer.addEventListener("scroll", handleScroll);
120
+ return () => scrollContainer.removeEventListener("scroll", handleScroll);
121
+ }
122
+ }, [handleScroll]);
123
+ console.log("📩 Messages:", messages);
124
+ console.log("📩 Messages Length:", messages?.length);
125
+
126
+ return (
127
+ <div className="chatMessages">
101
128
  {messages?.length > 0 ? (
102
129
  messages?.map((message: any) =>
103
130
  // Check if the message object is valid and has an _id before rendering
@@ -112,8 +139,11 @@ const Messages = () => {
112
139
  Send a message to start the conversation
113
140
  </p>
114
141
  )}
142
+
143
+ <div ref={ref} className="my-8">
144
+ {isFetchingNextPage ? '<Loading isLoading={isFetchingNextPage} /> ': null}
145
+ </div>
115
146
  </div>
116
- );
147
+ );
117
148
  };
118
149
  export default Messages;
119
-
@@ -13,16 +13,15 @@ console.log(conversation);
13
13
  const handleSelectConversation = async () => {
14
14
  setSelectedConversation(conversation);
15
15
 
16
- const lastMessageId = conversation.lastMessageId; // You should have this in conversation data
16
+ const unreadMessages = conversation.unreadMessageIds || []; // Get all unread message IDs
17
+
18
+ if (unreadMessages.length > 0) {
19
+ console.log("Emitting messageRead for messages:", unreadMessages);
17
20
 
18
- if (lastMessageId) {
19
- // ✅ Notify server via socket
20
21
  socket.emit("messageRead", {
21
- messageId: lastMessageId,
22
- receiverId: conversation.participantDetails._id, // or receiverId
22
+ messageIds: unreadMessages, // Send all unread message IDs
23
+ receiverId: conversation.participantDetails._id,
23
24
  });
24
-
25
-
26
25
  }
27
26
  };
28
27
 
@@ -34,7 +33,7 @@ const handleSelectConversation = async () => {
34
33
  <div
35
34
  className={` chatSidebarConversationMain
36
35
  `}
37
- onClick={() => setSelectedConversation(conversation)}
36
+ onClick={handleSelectConversation}
38
37
  >
39
38
  <img
40
39
  className="chatSidebarConversationImg"
@@ -1,16 +1,17 @@
1
1
  // src/declarations.d.ts
2
2
  declare module '*.css' {
3
- const content: string;
4
- export default content;
5
- }
3
+ const content: string;
4
+ export default content;
5
+ }
6
6
 
7
7
  declare module '*.png' {
8
- const content: string;
9
- export default content;
10
- }
8
+ const content: string;
9
+ export default content;
10
+ }
11
11
 
12
12
  declare module '*.svg' {
13
- const content: string;
14
- export default content;
15
- }
16
-
13
+ const content: string;
14
+ export default content;
15
+ }
16
+
17
+ declare module '@pubuduth-aplicy/chat-ui';
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { useQuery } from "@tanstack/react-query";
3
3
  import { getAllConversationData } from "../../service/sidebarApi";
4
- import { fetchMessages } from "../../service/messageService";
4
+ // import { fetchMessages } from "../../service/messageService";
5
5
 
6
6
 
7
7
  export const useGetConversations = (id: any) => {
@@ -15,12 +15,12 @@ export const useGetConversations = (id: any) => {
15
15
  });
16
16
  };
17
17
 
18
- export const useMessages = (chatId: string| undefined, userid: string) => {
19
- return useQuery({
20
- queryKey: ['messages', chatId, userid],
21
- queryFn: () => {
22
- console.log('Fetching messages for:', chatId, userid);
23
- return fetchMessages(chatId, userid);
24
- },
25
- });
26
- };
18
+ // export const useMessages = (chatId: string| undefined, userid: string) => {
19
+ // return useQuery({
20
+ // queryKey: ['messages', chatId, userid],
21
+ // queryFn: () => {
22
+ // console.log('Fetching messages for:', chatId, userid);
23
+ // return fetchMessages(chatId, userid);
24
+ // },
25
+ // });
26
+ // };
@@ -10,9 +10,11 @@ export const sendMessage = async ({ chatId,senderId, message }: { chatId: any; s
10
10
  };
11
11
 
12
12
 
13
- export const fetchMessages = async (chatId: string|undefined, userid: string) => {
13
+ export const fetchMessages = async (chatId: string|undefined, userid: string,pagenum:number) => {
14
14
  try {
15
- const response = await apiClient.get(`${Path.getmessage}/${chatId}/${userid}`);
15
+ const response = await apiClient.get(`${Path.getmessage}/${chatId}/${userid}`,{
16
+ params: { pagenum, limit: 20 },
17
+ });
16
18
  console.log(response); // Check the full response
17
19
  return response.data; // Ensure 'data' exists or adjust accordingly
18
20
  } catch (error) {