@pubuduth-aplicy/chat-ui 2.1.40 → 2.1.42
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 +2 -2
- package/src/components/messages/Messages.tsx +149 -79
- package/src/declarations.d.ts +15 -4
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubuduth-aplicy/chat-ui",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.42",
|
|
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": "",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "src/index.tsx",
|
|
9
|
-
"types": "
|
|
9
|
+
"types": "src/declarations.d.ts",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc ",
|
|
12
12
|
"prepare": "npm run build"
|
|
@@ -7,7 +7,7 @@ import useChatUIStore from "../../stores/Zustant";
|
|
|
7
7
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
8
8
|
import { fetchMessages } from "../../service/messageService";
|
|
9
9
|
import { useInView } from "react-intersection-observer";
|
|
10
|
-
import Loader from "../
|
|
10
|
+
import Loader from "../Loader";
|
|
11
11
|
|
|
12
12
|
const Messages = () => {
|
|
13
13
|
const { selectedConversation, setMessages, messages } = useChatUIStore();
|
|
@@ -16,68 +16,135 @@ const Messages = () => {
|
|
|
16
16
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
17
17
|
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
|
18
18
|
const [isAutoScrolling, setIsAutoScrolling] = useState(false);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const [prevMessagesLength, setPrevMessagesLength] = useState(0);
|
|
20
|
+
const loadingRef = useRef(false);
|
|
21
21
|
// const { data, isLoading, isError, error } = useMessages(selectedConversation?._id, userId);
|
|
22
22
|
|
|
23
23
|
const lastMessageRef = useRef<HTMLDivElement>(null);
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// queryKey: ["messages", selectedConversation?._id, userId],
|
|
28
|
-
// queryFn: ({ pageParam = 1 }) =>
|
|
29
|
-
// fetchMessages(selectedConversation?._id, userId, pageParam),
|
|
30
|
-
// initialPageParam: 1,
|
|
31
|
-
// getNextPageParam: (lastPage, allPages) => {
|
|
32
|
-
// return lastPage.messages.length ? allPages.length + 1: undefined;
|
|
33
|
-
// },
|
|
34
|
-
// });
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const {
|
|
38
|
-
data,
|
|
39
|
-
fetchNextPage,
|
|
40
|
-
hasNextPage,
|
|
41
|
-
isFetchingNextPage,
|
|
42
|
-
isLoading,
|
|
43
|
-
} = useInfiniteQuery({
|
|
25
|
+
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
|
26
|
+
useInfiniteQuery({
|
|
44
27
|
queryKey: ["messages", selectedConversation?._id, userId],
|
|
45
28
|
queryFn: ({ pageParam = 1 }) =>
|
|
46
29
|
fetchMessages(selectedConversation?._id, userId, pageParam),
|
|
47
|
-
getNextPageParam: (lastPage) =>
|
|
48
|
-
|
|
49
|
-
const allMessages = data.pages.flatMap(page => page.messages);
|
|
50
|
-
setMessages(allMessages);
|
|
51
|
-
|
|
52
|
-
// Scroll to bottom on initial load
|
|
53
|
-
if (isInitialLoad && scrollContainerRef.current) {
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
scrollContainerRef.current?.scrollTo({
|
|
56
|
-
top: scrollContainerRef.current.scrollHeight,
|
|
57
|
-
behavior: 'auto'
|
|
58
|
-
});
|
|
59
|
-
setIsInitialLoad(false);
|
|
60
|
-
}, 100);
|
|
61
|
-
}
|
|
30
|
+
getNextPageParam: (lastPage) => {
|
|
31
|
+
return lastPage.nextPage; // Use the nextPage from API response
|
|
62
32
|
},
|
|
33
|
+
initialPageParam: 1,
|
|
63
34
|
});
|
|
64
35
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
36
|
+
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
37
|
+
const scrollHeightBeforeLoad = useRef(0);
|
|
38
|
+
|
|
39
|
+
const handleScroll = useCallback(() => {
|
|
40
|
+
if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
|
|
41
|
+
|
|
42
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current;
|
|
43
|
+
|
|
44
|
+
// Load more when scrolled near the top (100px threshold)
|
|
45
|
+
if (scrollTop < 100) {
|
|
46
|
+
// Save current scroll height before loading
|
|
47
|
+
scrollHeightBeforeLoad.current = scrollHeight;
|
|
48
|
+
setIsLoadingMore(true);
|
|
49
|
+
fetchNextPage().finally(() => setIsLoadingMore(false));
|
|
50
|
+
}
|
|
51
|
+
}, [fetchNextPage, isFetchingNextPage, hasNextPage]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (isLoadingMore && scrollContainerRef.current && scrollHeightBeforeLoad.current) {
|
|
55
|
+
// After new messages are loaded, adjust scroll position to maintain the same view
|
|
56
|
+
const container = scrollContainerRef.current;
|
|
57
|
+
const newScrollHeight = container.scrollHeight;
|
|
58
|
+
container.scrollTop = newScrollHeight - scrollHeightBeforeLoad.current;
|
|
59
|
+
}
|
|
60
|
+
}, [messages.length, isLoadingMore]);
|
|
61
|
+
|
|
62
|
+
// Scroll to bottom for new messages
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (messages.length > 0 && !isLoadingMore) {
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
67
|
+
}, 100);
|
|
68
|
+
}
|
|
69
|
+
}, [messages.length, isLoadingMore]);
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
// const {
|
|
73
|
+
// data,
|
|
74
|
+
// fetchNextPage,
|
|
75
|
+
// hasNextPage,
|
|
76
|
+
// isFetchingNextPage,
|
|
77
|
+
// isLoading,
|
|
78
|
+
// } = useInfiniteQuery({
|
|
79
|
+
// queryKey: ["messages", selectedConversation?._id, userId],
|
|
80
|
+
// queryFn: ({ pageParam = 1 }) =>
|
|
81
|
+
// fetchMessages(selectedConversation?._id, userId, pageParam),
|
|
82
|
+
// getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
|
|
83
|
+
// onSuccess: (data) => {
|
|
84
|
+
// const allMessages = data.pages.flatMap(page => page.messages);
|
|
85
|
+
// setMessages(allMessages);
|
|
86
|
+
|
|
87
|
+
// // Scroll to bottom on initial load
|
|
88
|
+
// if (isInitialLoad && scrollContainerRef.current) {
|
|
89
|
+
// setTimeout(() => {
|
|
90
|
+
// scrollContainerRef.current?.scrollTo({
|
|
91
|
+
// top: scrollContainerRef.current.scrollHeight,
|
|
92
|
+
// behavior: 'auto'
|
|
93
|
+
// });
|
|
94
|
+
// setIsInitialLoad(false);
|
|
95
|
+
// }, 100);
|
|
96
|
+
// }
|
|
97
|
+
// },
|
|
98
|
+
// });
|
|
99
|
+
|
|
100
|
+
// Maintain scroll position when loading older messages
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// useEffect(() => {
|
|
104
|
+
// console.log('scrollContainerRef',scrollContainerRef.current);
|
|
105
|
+
|
|
106
|
+
// if (!scrollContainerRef.current || isInitialLoad) return;
|
|
107
|
+
|
|
108
|
+
// if (isFetchingNextPage) {
|
|
109
|
+
// // Store current scroll position before loading
|
|
110
|
+
// const container = scrollContainerRef.current;
|
|
111
|
+
// const scrollTopBefore = container.scrollTop;
|
|
112
|
+
// const scrollHeightBefore = container.scrollHeight;
|
|
113
|
+
// console.log(container,'scrollHeightBefore',scrollHeightBefore,'container',scrollTopBefore);
|
|
114
|
+
|
|
115
|
+
// loadingRef.current = true;
|
|
116
|
+
|
|
117
|
+
// return () => {
|
|
118
|
+
// // After messages are loaded, adjust scroll position
|
|
119
|
+
// requestAnimationFrame(() => {
|
|
120
|
+
// container.scrollTop = container.scrollHeight - scrollHeightBefore + scrollTopBefore;
|
|
121
|
+
// loadingRef.current = false;
|
|
122
|
+
// });
|
|
123
|
+
// };
|
|
124
|
+
// }
|
|
125
|
+
// }, [isFetchingNextPage, isInitialLoad]);
|
|
126
|
+
|
|
127
|
+
// Add this useEffect to handle new messages
|
|
128
|
+
// useEffect(() => {
|
|
129
|
+
// if (!scrollContainerRef.current || loadingRef.current) return;
|
|
130
|
+
|
|
131
|
+
// const container = scrollContainerRef.current;
|
|
132
|
+
// console.log("container",container);
|
|
133
|
+
|
|
134
|
+
// const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 100;
|
|
135
|
+
|
|
136
|
+
// // Only auto-scroll if we're near bottom or it's a new message
|
|
137
|
+
// if (isNearBottom || messages.length > prevMessagesLength) {
|
|
138
|
+
// setIsAutoScrolling(true);
|
|
139
|
+
// container.scrollTo({
|
|
140
|
+
// top: container.scrollHeight,
|
|
141
|
+
// behavior: messages.length > prevMessagesLength ? 'smooth' : 'auto'
|
|
142
|
+
// });
|
|
143
|
+
// setTimeout(() => setIsAutoScrolling(false), 500);
|
|
144
|
+
// }
|
|
145
|
+
|
|
146
|
+
// setPrevMessagesLength(messages.length);
|
|
147
|
+
// }, [messages.length]);
|
|
81
148
|
|
|
82
149
|
useEffect(() => {
|
|
83
150
|
if (inView) {
|
|
@@ -95,7 +162,9 @@ const {
|
|
|
95
162
|
|
|
96
163
|
useEffect(() => {
|
|
97
164
|
if (data) {
|
|
98
|
-
|
|
165
|
+
console.log('message fetching data',data);
|
|
166
|
+
|
|
167
|
+
const allMessages = data.pages.flatMap(page => page.messages).reverse();
|
|
99
168
|
setMessages(allMessages);
|
|
100
169
|
}
|
|
101
170
|
}, [data]);
|
|
@@ -172,23 +241,23 @@ useEffect(() => {
|
|
|
172
241
|
return () => observer.disconnect();
|
|
173
242
|
}, [messages, socket]);
|
|
174
243
|
|
|
175
|
-
const handleScroll = useCallback(() => {
|
|
176
|
-
|
|
244
|
+
// const handleScroll = useCallback(() => {
|
|
245
|
+
// if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
|
|
177
246
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}, [fetchNextPage, isFetchingNextPage, hasNextPage]);
|
|
247
|
+
// const { scrollTop } = scrollContainerRef.current;
|
|
248
|
+
// // Load more when scrolled near the top (10% threshold)
|
|
249
|
+
// if (scrollTop < 100) {
|
|
250
|
+
// fetchNextPage();
|
|
251
|
+
// }
|
|
252
|
+
// }, [fetchNextPage, isFetchingNextPage, hasNextPage]);
|
|
184
253
|
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}, [handleScroll]);
|
|
254
|
+
// useEffect(() => {
|
|
255
|
+
// const container = scrollContainerRef.current;
|
|
256
|
+
// if (container) {
|
|
257
|
+
// container.addEventListener('scroll', handleScroll);
|
|
258
|
+
// return () => container.removeEventListener('scroll', handleScroll);
|
|
259
|
+
// }
|
|
260
|
+
// }, [handleScroll]);
|
|
192
261
|
|
|
193
262
|
// useEffect(() => {
|
|
194
263
|
// const scrollContainer = scrollContainerRef.current;
|
|
@@ -197,28 +266,29 @@ useEffect(() => {
|
|
|
197
266
|
// return () => scrollContainer.removeEventListener("scroll", handleScroll);
|
|
198
267
|
// }
|
|
199
268
|
// }, [handleScroll]);
|
|
200
|
-
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
console.log("📩 Messages:", messages);
|
|
201
272
|
console.log("📩 Messages Length:", messages?.length);
|
|
202
273
|
|
|
203
|
-
|
|
204
|
-
return <Loader />;
|
|
205
|
-
}
|
|
274
|
+
|
|
206
275
|
|
|
207
276
|
return (
|
|
208
277
|
<div className="chatMessages"
|
|
209
278
|
ref={scrollContainerRef}
|
|
210
279
|
style={{ overflowY: 'auto', height: '100%', position: 'relative' }}
|
|
211
280
|
>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
)}
|
|
281
|
+
|
|
282
|
+
<div ref={ref} className="my-8">
|
|
283
|
+
{isFetchingNextPage ? <Loader /> : null}
|
|
284
|
+
</div>
|
|
217
285
|
{messages?.length > 0 ? (
|
|
218
286
|
messages?.map((message: any) =>
|
|
219
287
|
// Check if the message object is valid and has an _id before rendering
|
|
220
288
|
message ? (
|
|
221
|
-
<div key={message._id} ref={lastMessageRef}
|
|
289
|
+
<div key={message._id} ref={lastMessageRef}
|
|
290
|
+
style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}
|
|
291
|
+
>
|
|
222
292
|
<Message message={message} />
|
|
223
293
|
</div>
|
|
224
294
|
) : null
|
package/src/declarations.d.ts
CHANGED
|
@@ -15,8 +15,19 @@ declare module '*.svg' {
|
|
|
15
15
|
export default content;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
declare module '@pubuduth-aplicy/chat-ui';
|
|
19
18
|
declare module '@pubuduth-aplicy/chat-ui' {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
import { ReactNode, FC } from 'react';
|
|
20
|
+
|
|
21
|
+
interface ChatProviderProps {
|
|
22
|
+
userId: string;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ChatProvider: FC<ChatProviderProps>;
|
|
27
|
+
export const Chat: FC;
|
|
28
|
+
export function useChatContext(): {
|
|
29
|
+
socket: any; // Replace 'any' with proper Socket type
|
|
30
|
+
userId: string;
|
|
31
|
+
onlineUsers: any[];
|
|
32
|
+
};
|
|
33
|
+
}
|