@pubuduth-aplicy/chat-ui 2.1.38 → 2.1.40
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 +1 -1
- package/src/components/Loader.tsx +17 -0
- package/src/components/messages/Messages.tsx +127 -285
- package/src/style/style.css +107 -1
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const Loader = () => {
|
|
2
|
+
return (
|
|
3
|
+
|
|
4
|
+
<div className="dot-spinner">
|
|
5
|
+
<div className="dot-spinner__dot" />
|
|
6
|
+
<div className="dot-spinner__dot" />
|
|
7
|
+
<div className="dot-spinner__dot" />
|
|
8
|
+
<div className="dot-spinner__dot" />
|
|
9
|
+
<div className="dot-spinner__dot" />
|
|
10
|
+
<div className="dot-spinner__dot" />
|
|
11
|
+
<div className="dot-spinner__dot" />
|
|
12
|
+
<div className="dot-spinner__dot" />
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default Loader
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import Message from "./Message";
|
|
4
|
+
import { useChatContext } from "../../providers/ChatProvider";
|
|
5
|
+
import { useMessages } from "../../hooks/queries/useChatApi";
|
|
6
|
+
import useChatUIStore from "../../stores/Zustant";
|
|
7
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
8
|
+
import { fetchMessages } from "../../service/messageService";
|
|
9
|
+
import { useInView } from "react-intersection-observer";
|
|
10
|
+
import Loader from "../loader";
|
|
11
|
+
|
|
12
|
+
const Messages = () => {
|
|
13
|
+
const { selectedConversation, setMessages, messages } = useChatUIStore();
|
|
14
|
+
const { userId, socket } = useChatContext();
|
|
15
|
+
const { ref, inView } = useInView();
|
|
16
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
17
|
+
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
|
18
|
+
const [isAutoScrolling, setIsAutoScrolling] = useState(false);
|
|
10
19
|
|
|
11
|
-
// const Messages = () => {
|
|
12
|
-
// const { selectedConversation, setMessages, messages } = useChatUIStore();
|
|
13
|
-
// const { userId, socket } = useChatContext();
|
|
14
|
-
// const { ref, inView } = useInView();
|
|
15
|
-
// const previousScrollHeight = useRef(0);
|
|
16
|
-
// const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
17
20
|
|
|
18
|
-
//
|
|
21
|
+
// const { data, isLoading, isError, error } = useMessages(selectedConversation?._id, userId);
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
const lastMessageRef = useRef<HTMLDivElement>(null);
|
|
21
24
|
|
|
22
25
|
// const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
|
23
26
|
// useInfiniteQuery({
|
|
@@ -30,183 +33,8 @@
|
|
|
30
33
|
// },
|
|
31
34
|
// });
|
|
32
35
|
|
|
33
|
-
// useEffect(() => {
|
|
34
|
-
// if (inView) {
|
|
35
|
-
// fetchNextPage();
|
|
36
|
-
// }
|
|
37
|
-
// }, [fetchNextPage, inView]);
|
|
38
|
-
|
|
39
|
-
// // useEffect(() => {
|
|
40
|
-
// // if (data) {
|
|
41
|
-
// // console.log(data,'message data');
|
|
42
|
-
|
|
43
|
-
// // setMessages(data.messages);
|
|
44
|
-
// // }
|
|
45
|
-
// // }, [selectedConversation?._id, data]);
|
|
46
|
-
|
|
47
|
-
// useEffect(() => {
|
|
48
|
-
// if (data) {
|
|
49
|
-
// const allMessages = data.pages.flatMap(page => page.messages);
|
|
50
|
-
// setMessages(allMessages);
|
|
51
|
-
|
|
52
|
-
// if (scrollContainerRef.current) {
|
|
53
|
-
// const newScrollHeight = scrollContainerRef.current.scrollHeight;
|
|
54
|
-
// scrollContainerRef.current.scrollTop = newScrollHeight - previousScrollHeight.current;
|
|
55
|
-
// }
|
|
56
|
-
// }
|
|
57
|
-
// }, [data]);
|
|
58
|
-
|
|
59
|
-
// // Listen for new messages from the server
|
|
60
|
-
// useEffect(() => {
|
|
61
|
-
// if (!socket || !selectedConversation?._id) return;
|
|
62
|
-
|
|
63
|
-
// // const handleNewMessage = (newMessage: any) => {
|
|
64
|
-
// // newMessage.shouldShake = true;
|
|
65
|
-
// // console.log("📩 New message received:", newMessage);
|
|
66
|
-
// // setMessages((prevMessages) => [...prevMessages, newMessage[1]]);
|
|
67
|
-
// // };
|
|
68
|
-
|
|
69
|
-
// const handleNewMessage = (newMessage: any) => {
|
|
70
|
-
// newMessage.shouldShake = true;
|
|
71
|
-
// console.log("📩 New message received:", newMessage);
|
|
72
|
-
// setMessages((prevMessages) => {
|
|
73
|
-
// const updatedMessages = [...prevMessages, newMessage];
|
|
74
|
-
// return [...new Map(updatedMessages.map(m => [m._id, m])).values()]; // Prevent duplicates
|
|
75
|
-
// });
|
|
76
|
-
// };
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// const handleStatusUpdate = ({
|
|
80
|
-
// messageId,
|
|
81
|
-
// status,
|
|
82
|
-
// }: {
|
|
83
|
-
// messageId: string;
|
|
84
|
-
// status: string;
|
|
85
|
-
// }) => {
|
|
86
|
-
// setMessages((prev) =>
|
|
87
|
-
// prev.map((msg) => (msg._id === messageId ? { ...msg, status } : msg))
|
|
88
|
-
// );
|
|
89
|
-
// };
|
|
90
|
-
|
|
91
|
-
// socket.on("newMessage", handleNewMessage);
|
|
92
|
-
// socket.on("messageStatusUpdated", handleStatusUpdate);
|
|
93
|
-
|
|
94
|
-
// return () => {
|
|
95
|
-
// socket.off("newMessage", handleNewMessage);
|
|
96
|
-
// socket.off("messageStatusUpdated", handleStatusUpdate);
|
|
97
|
-
// };
|
|
98
|
-
// }, [socket, setMessages, messages]);
|
|
99
36
|
|
|
100
|
-
|
|
101
|
-
// // setTimeout(() => {
|
|
102
|
-
// // lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
103
|
-
// // }, 100);
|
|
104
|
-
// // }, [messages]);
|
|
105
|
-
|
|
106
|
-
// useEffect(() => {
|
|
107
|
-
// if (!scrollContainerRef.current) return;
|
|
108
|
-
|
|
109
|
-
// const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current;
|
|
110
|
-
// const isAtBottom = scrollHeight - scrollTop <= clientHeight + 100;
|
|
111
|
-
|
|
112
|
-
// if (isAtBottom) {
|
|
113
|
-
// setTimeout(() => {
|
|
114
|
-
// lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
115
|
-
// }, 100);
|
|
116
|
-
// }
|
|
117
|
-
// }, [messages]);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// useEffect(() => {
|
|
121
|
-
// if (!socket || !messages.length) return;
|
|
122
|
-
|
|
123
|
-
// const observer = new IntersectionObserver(
|
|
124
|
-
// (entries) => {
|
|
125
|
-
// entries.forEach((entry) => {
|
|
126
|
-
// if (entry.isIntersecting) {
|
|
127
|
-
// const messageId = entry.target.getAttribute("data-message-id");
|
|
128
|
-
// if (messageId) {
|
|
129
|
-
// socket.emit("confirmDelivery", { messageId });
|
|
130
|
-
// }
|
|
131
|
-
// }
|
|
132
|
-
// });
|
|
133
|
-
// },
|
|
134
|
-
// { threshold: 0.5 }
|
|
135
|
-
// );
|
|
136
|
-
|
|
137
|
-
// const messageElements = document.querySelectorAll("[data-message-id]");
|
|
138
|
-
// messageElements.forEach((el) => observer.observe(el));
|
|
139
|
-
|
|
140
|
-
// return () => observer.disconnect();
|
|
141
|
-
// }, [messages, socket]);
|
|
142
|
-
|
|
143
|
-
// const handleScroll = useCallback(() => {
|
|
144
|
-
// if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
|
|
145
|
-
|
|
146
|
-
// const { scrollTop } = scrollContainerRef.current;
|
|
147
|
-
|
|
148
|
-
// if (scrollTop < 50) { // Fetch next page only if close to the top
|
|
149
|
-
// fetchNextPage();
|
|
150
|
-
// }
|
|
151
|
-
// }, [fetchNextPage, isFetchingNextPage, hasNextPage]);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// useEffect(() => {
|
|
156
|
-
// const scrollContainer = scrollContainerRef.current;
|
|
157
|
-
// if (scrollContainer) {
|
|
158
|
-
// scrollContainer.addEventListener("scroll", handleScroll);
|
|
159
|
-
// return () => scrollContainer.removeEventListener("scroll", handleScroll);
|
|
160
|
-
// }
|
|
161
|
-
// }, [handleScroll]);
|
|
162
|
-
|
|
163
|
-
// console.log("📩 Messages:", messages);
|
|
164
|
-
// console.log("📩 Messages Length:", messages?.length);
|
|
165
|
-
|
|
166
|
-
// return (
|
|
167
|
-
// <div className="chatMessages">
|
|
168
|
-
|
|
169
|
-
// <div ref={ref} className="my-8">
|
|
170
|
-
// {isFetchingNextPage ? '<Loading isLoading={isFetchingNextPage} /> ': null}
|
|
171
|
-
// </div>
|
|
172
|
-
// {messages?.length > 0 ? (
|
|
173
|
-
// messages?.map((message: any) =>
|
|
174
|
-
// // Check if the message object is valid and has an _id before rendering
|
|
175
|
-
// message ? (
|
|
176
|
-
// <div key={message._id} ref={lastMessageRef}>
|
|
177
|
-
// <Message message={message} />
|
|
178
|
-
// </div>
|
|
179
|
-
// ) : null
|
|
180
|
-
// )
|
|
181
|
-
// ) : (
|
|
182
|
-
// <p style={{ textAlign: "center" }}>
|
|
183
|
-
// Send a message to start the conversation
|
|
184
|
-
// </p>
|
|
185
|
-
// )}
|
|
186
|
-
|
|
187
|
-
// </div>
|
|
188
|
-
// );
|
|
189
|
-
// };
|
|
190
|
-
// export default Messages;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
194
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
195
|
-
import Message from "./Message";
|
|
196
|
-
import { useChatContext } from "../../providers/ChatProvider";
|
|
197
|
-
import { useMessages } from "../../hooks/queries/useChatApi";
|
|
198
|
-
import useChatUIStore from "../../stores/Zustant";
|
|
199
|
-
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
200
|
-
import { fetchMessages } from "../../service/messageService";// Assuming you have a loading spinner component
|
|
201
|
-
|
|
202
|
-
const Messages = () => {
|
|
203
|
-
const { selectedConversation, setMessages, messages } = useChatUIStore();
|
|
204
|
-
const { userId, socket } = useChatContext();
|
|
205
|
-
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
206
|
-
const [isAutoScrolling, setIsAutoScrolling] = useState(false);
|
|
207
|
-
const [initialLoad, setInitialLoad] = useState(true);
|
|
208
|
-
|
|
209
|
-
const {
|
|
37
|
+
const {
|
|
210
38
|
data,
|
|
211
39
|
fetchNextPage,
|
|
212
40
|
hasNextPage,
|
|
@@ -216,76 +44,81 @@ const Messages = () => {
|
|
|
216
44
|
queryKey: ["messages", selectedConversation?._id, userId],
|
|
217
45
|
queryFn: ({ pageParam = 1 }) =>
|
|
218
46
|
fetchMessages(selectedConversation?._id, userId, pageParam),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return lastPage.messages.length ? allPages.length + 1 : undefined;
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
// Handle merging paginated data with messages
|
|
226
|
-
useEffect(() => {
|
|
227
|
-
if (data) {
|
|
47
|
+
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
|
|
48
|
+
onSuccess: (data) => {
|
|
228
49
|
const allMessages = data.pages.flatMap(page => page.messages);
|
|
229
50
|
setMessages(allMessages);
|
|
230
|
-
}
|
|
231
|
-
}, [data, setMessages]);
|
|
232
|
-
|
|
233
|
-
// Handle scroll position when new messages are loaded
|
|
234
|
-
useEffect(() => {
|
|
235
|
-
if (!scrollContainerRef.current || initialLoad) return;
|
|
236
|
-
|
|
237
|
-
const container = scrollContainerRef.current;
|
|
238
|
-
if (isFetchingNextPage) {
|
|
239
|
-
// Store current scroll position before loading more messages
|
|
240
|
-
const scrollTopBefore = container.scrollTop;
|
|
241
|
-
const scrollHeightBefore = container.scrollHeight;
|
|
242
51
|
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}, [messages.length, initialLoad]);
|
|
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
|
+
}
|
|
62
|
+
},
|
|
63
|
+
});
|
|
257
64
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
65
|
+
// Auto-scroll to bottom when new message arrives and user is near bottom
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!scrollContainerRef.current || isAutoScrolling) return;
|
|
68
|
+
|
|
69
|
+
const container = scrollContainerRef.current;
|
|
70
|
+
const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 100;
|
|
71
|
+
|
|
72
|
+
if (isNearBottom) {
|
|
73
|
+
setIsAutoScrolling(true);
|
|
74
|
+
container.scrollTo({
|
|
75
|
+
top: container.scrollHeight,
|
|
76
|
+
behavior: "smooth"
|
|
77
|
+
});
|
|
78
|
+
setTimeout(() => setIsAutoScrolling(false), 500);
|
|
79
|
+
}
|
|
80
|
+
}, [messages.length, isAutoScrolling]);
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (inView) {
|
|
84
|
+
fetchNextPage();
|
|
85
|
+
}
|
|
86
|
+
}, [fetchNextPage, inView]);
|
|
261
87
|
|
|
262
|
-
|
|
263
|
-
|
|
88
|
+
// useEffect(() => {
|
|
89
|
+
// if (data) {
|
|
90
|
+
// console.log(data,'message data');
|
|
91
|
+
|
|
92
|
+
// setMessages(data.messages);
|
|
93
|
+
// }
|
|
94
|
+
// }, [selectedConversation?._id, data]);
|
|
264
95
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
setTimeout(() => setIsAutoScrolling(false), 500);
|
|
272
|
-
}
|
|
273
|
-
}, [messages.length, isAutoScrolling]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (data) {
|
|
98
|
+
const allMessages = data.pages.flatMap(page => page.messages);
|
|
99
|
+
setMessages(allMessages);
|
|
100
|
+
}
|
|
101
|
+
}, [data]);
|
|
274
102
|
|
|
275
|
-
//
|
|
103
|
+
// Listen for new messages from the server
|
|
276
104
|
useEffect(() => {
|
|
277
105
|
if (!socket || !selectedConversation?._id) return;
|
|
278
106
|
|
|
279
|
-
const handleNewMessage = (newMessage: any) => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
107
|
+
// const handleNewMessage = (newMessage: any) => {
|
|
108
|
+
// newMessage.shouldShake = true;
|
|
109
|
+
// console.log("📩 New message received:", newMessage);
|
|
110
|
+
// setMessages((prevMessages) => [...prevMessages, newMessage[1]]);
|
|
111
|
+
// };
|
|
112
|
+
|
|
113
|
+
const handleNewMessage = (newMessage: any) => {
|
|
114
|
+
newMessage.shouldShake = true;
|
|
115
|
+
console.log("📩 New message received:", newMessage);
|
|
116
|
+
setMessages((prevMessages) => {
|
|
117
|
+
const updatedMessages = [...prevMessages, newMessage];
|
|
118
|
+
return [...new Map(updatedMessages.map(m => [m._id, m])).values()]; // Prevent duplicates
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
289
122
|
|
|
290
123
|
const handleStatusUpdate = ({
|
|
291
124
|
messageId,
|
|
@@ -306,9 +139,16 @@ const Messages = () => {
|
|
|
306
139
|
socket.off("newMessage", handleNewMessage);
|
|
307
140
|
socket.off("messageStatusUpdated", handleStatusUpdate);
|
|
308
141
|
};
|
|
309
|
-
}, [socket, setMessages,
|
|
142
|
+
}, [socket, setMessages, messages]);
|
|
143
|
+
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (messages.length > 0) {
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
148
|
+
}, 100);
|
|
149
|
+
}
|
|
150
|
+
}, [messages.length]);
|
|
310
151
|
|
|
311
|
-
// Delivery confirmation observer
|
|
312
152
|
useEffect(() => {
|
|
313
153
|
if (!socket || !messages.length) return;
|
|
314
154
|
|
|
@@ -332,62 +172,64 @@ const Messages = () => {
|
|
|
332
172
|
return () => observer.disconnect();
|
|
333
173
|
}, [messages, socket]);
|
|
334
174
|
|
|
335
|
-
// Scroll handler for infinite loading
|
|
336
175
|
const handleScroll = useCallback(() => {
|
|
337
176
|
if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
|
|
338
177
|
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// Load more messages when scrolled near the top
|
|
343
|
-
if (scrollTop < 100 && !isFetchingNextPage) {
|
|
178
|
+
const { scrollTop } = scrollContainerRef.current;
|
|
179
|
+
// Load more when scrolled near the top (10% threshold)
|
|
180
|
+
if (scrollTop < 100) {
|
|
344
181
|
fetchNextPage();
|
|
345
182
|
}
|
|
346
183
|
}, [fetchNextPage, isFetchingNextPage, hasNextPage]);
|
|
347
184
|
|
|
348
185
|
useEffect(() => {
|
|
349
|
-
const
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
return () =>
|
|
186
|
+
const container = scrollContainerRef.current;
|
|
187
|
+
if (container) {
|
|
188
|
+
container.addEventListener('scroll', handleScroll);
|
|
189
|
+
return () => container.removeEventListener('scroll', handleScroll);
|
|
353
190
|
}
|
|
354
191
|
}, [handleScroll]);
|
|
355
192
|
|
|
193
|
+
// useEffect(() => {
|
|
194
|
+
// const scrollContainer = scrollContainerRef.current;
|
|
195
|
+
// if (scrollContainer) {
|
|
196
|
+
// scrollContainer.addEventListener("scroll", handleScroll);
|
|
197
|
+
// return () => scrollContainer.removeEventListener("scroll", handleScroll);
|
|
198
|
+
// }
|
|
199
|
+
// }, [handleScroll]);
|
|
200
|
+
console.log("📩 Messages:", messages);
|
|
201
|
+
console.log("📩 Messages Length:", messages?.length);
|
|
202
|
+
|
|
356
203
|
if (isLoading && !messages.length) {
|
|
357
|
-
return <
|
|
204
|
+
return <Loader />;
|
|
358
205
|
}
|
|
359
206
|
|
|
360
207
|
return (
|
|
361
|
-
<div
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
{isFetchingNextPage && (
|
|
208
|
+
<div className="chatMessages"
|
|
209
|
+
ref={scrollContainerRef}
|
|
210
|
+
style={{ overflowY: 'auto', height: '100%', position: 'relative' }}
|
|
211
|
+
>
|
|
212
|
+
{isFetchingNextPage && (
|
|
367
213
|
<div className="loading-indicator">
|
|
368
|
-
|
|
214
|
+
<Loader/>
|
|
369
215
|
</div>
|
|
370
216
|
)}
|
|
371
|
-
|
|
372
217
|
{messages?.length > 0 ? (
|
|
373
|
-
messages
|
|
374
|
-
message
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
data-message-id={message._id}
|
|
378
|
-
ref={messages.indexOf(message) === messages.length - 1 ? lastMessageRef : null}
|
|
379
|
-
>
|
|
218
|
+
messages?.map((message: any) =>
|
|
219
|
+
// Check if the message object is valid and has an _id before rendering
|
|
220
|
+
message ? (
|
|
221
|
+
<div key={message._id} ref={lastMessageRef}>
|
|
380
222
|
<Message message={message} />
|
|
381
223
|
</div>
|
|
382
|
-
)
|
|
383
|
-
)
|
|
224
|
+
) : null
|
|
225
|
+
)
|
|
384
226
|
) : (
|
|
385
227
|
<p style={{ textAlign: "center" }}>
|
|
386
228
|
Send a message to start the conversation
|
|
387
229
|
</p>
|
|
388
230
|
)}
|
|
231
|
+
|
|
389
232
|
</div>
|
|
390
233
|
);
|
|
391
234
|
};
|
|
392
|
-
|
|
393
|
-
export default Messages;
|
|
235
|
+
export default Messages;
|
package/src/style/style.css
CHANGED
|
@@ -442,4 +442,110 @@
|
|
|
442
442
|
50% {
|
|
443
443
|
transform: translateY(-3px);
|
|
444
444
|
}
|
|
445
|
-
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* From Uiverse.io by abrahamcalsin */
|
|
448
|
+
.dot-spinner {
|
|
449
|
+
--uib-size: 2.8rem;
|
|
450
|
+
--uib-speed: .9s;
|
|
451
|
+
--uib-color: #183153;
|
|
452
|
+
position: relative;
|
|
453
|
+
display: flex;
|
|
454
|
+
align-items: center;
|
|
455
|
+
justify-content: flex-start;
|
|
456
|
+
height: var(--uib-size);
|
|
457
|
+
width: var(--uib-size);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.dot-spinner__dot {
|
|
461
|
+
position: absolute;
|
|
462
|
+
top: 0;
|
|
463
|
+
left: 0;
|
|
464
|
+
display: flex;
|
|
465
|
+
align-items: center;
|
|
466
|
+
justify-content: flex-start;
|
|
467
|
+
height: 100%;
|
|
468
|
+
width: 100%;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.dot-spinner__dot::before {
|
|
472
|
+
content: '';
|
|
473
|
+
height: 20%;
|
|
474
|
+
width: 20%;
|
|
475
|
+
border-radius: 50%;
|
|
476
|
+
background-color: var(--uib-color);
|
|
477
|
+
transform: scale(0);
|
|
478
|
+
opacity: 0.5;
|
|
479
|
+
animation: pulse0112 calc(var(--uib-speed) * 1.111) ease-in-out infinite;
|
|
480
|
+
box-shadow: 0 0 20px rgba(18, 31, 53, 0.3);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.dot-spinner__dot:nth-child(2) {
|
|
484
|
+
transform: rotate(45deg);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.dot-spinner__dot:nth-child(2)::before {
|
|
488
|
+
animation-delay: calc(var(--uib-speed) * -0.875);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.dot-spinner__dot:nth-child(3) {
|
|
492
|
+
transform: rotate(90deg);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.dot-spinner__dot:nth-child(3)::before {
|
|
496
|
+
animation-delay: calc(var(--uib-speed) * -0.75);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.dot-spinner__dot:nth-child(4) {
|
|
500
|
+
transform: rotate(135deg);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.dot-spinner__dot:nth-child(4)::before {
|
|
504
|
+
animation-delay: calc(var(--uib-speed) * -0.625);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.dot-spinner__dot:nth-child(5) {
|
|
508
|
+
transform: rotate(180deg);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.dot-spinner__dot:nth-child(5)::before {
|
|
512
|
+
animation-delay: calc(var(--uib-speed) * -0.5);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.dot-spinner__dot:nth-child(6) {
|
|
516
|
+
transform: rotate(225deg);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.dot-spinner__dot:nth-child(6)::before {
|
|
520
|
+
animation-delay: calc(var(--uib-speed) * -0.375);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.dot-spinner__dot:nth-child(7) {
|
|
524
|
+
transform: rotate(270deg);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.dot-spinner__dot:nth-child(7)::before {
|
|
528
|
+
animation-delay: calc(var(--uib-speed) * -0.25);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.dot-spinner__dot:nth-child(8) {
|
|
532
|
+
transform: rotate(315deg);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.dot-spinner__dot:nth-child(8)::before {
|
|
536
|
+
animation-delay: calc(var(--uib-speed) * -0.125);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
@keyframes pulse0112 {
|
|
540
|
+
0%,
|
|
541
|
+
100% {
|
|
542
|
+
transform: scale(0);
|
|
543
|
+
opacity: 0.5;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
50% {
|
|
547
|
+
transform: scale(1);
|
|
548
|
+
opacity: 1;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|