@pubuduth-aplicy/chat-ui 2.1.56 → 2.1.57

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.
@@ -7,69 +7,78 @@ import { useChatContext } from "../../providers/ChatProvider";
7
7
  // import { Chat, CaretLeft } from "@phosphor-icons/react";
8
8
  // import { useAuthContext } from "../../context/AuthContext";
9
9
 
10
-
11
10
  const MessageContainer = () => {
12
- const { selectedConversation, setSelectedConversation ,onlineUsers, setOnlineUsers } = useChatUIStore();
13
- const {socket} = useChatContext();
11
+ const {
12
+ selectedConversation,
13
+ setSelectedConversation,
14
+ onlineUsers,
15
+ setOnlineUsers,
16
+ } = useChatUIStore();
17
+ const { socket } = useChatContext();
14
18
 
15
- useEffect(() => {
16
- if (selectedConversation?._id && socket) {
17
- socket.emit("joinChat", selectedConversation._id); // Join chat room
18
- }
19
- }, [selectedConversation?._id, socket]);
20
-
19
+ useEffect(() => {
20
+ if (selectedConversation?._id && socket) {
21
+ socket.emit("joinChat", selectedConversation._id); // Join chat room
22
+ }
23
+ }, [selectedConversation?._id, socket]);
21
24
 
22
- useEffect(() => {
23
- if (!socket) return;
25
+ useEffect(() => {
26
+ if (!socket) return;
24
27
 
25
- const handleOnlineUsers = (users: string[]) => {
26
- setOnlineUsers(users);
27
- };
28
+ const handleOnlineUsers = (users: string[]) => {
29
+ setOnlineUsers(users);
30
+ };
28
31
 
29
- socket.on("getOnlineUsers", handleOnlineUsers);
32
+ socket.on("getOnlineUsers", handleOnlineUsers);
30
33
 
31
- return () => {
32
- socket.off("getOnlineUsers", handleOnlineUsers);
33
- };
34
- }, [socket, setOnlineUsers]);
34
+ return () => {
35
+ socket.off("getOnlineUsers", handleOnlineUsers);
36
+ };
37
+ }, [socket, setOnlineUsers]);
35
38
 
36
- const isUserOnline = selectedConversation?.participantDetails?._id &&
39
+ const isUserOnline =
40
+ selectedConversation?.participantDetails?._id &&
37
41
  onlineUsers?.includes(selectedConversation.participantDetails._id);
38
42
 
43
+ useEffect(() => {
44
+ // cleanup function (unmounts)
45
+ return () => setSelectedConversation(null);
46
+ }, [setSelectedConversation]);
39
47
 
40
- useEffect(() => {
41
- // cleanup function (unmounts)
42
- return () => setSelectedConversation(null);
43
- }, [setSelectedConversation]);
44
-
45
- return (
46
- <div className='chatMessageContainer'>
47
-
48
- {!selectedConversation ? (
49
- <EmptyInbox />
50
- ) : (
51
- <>
52
- <div className="chatMessageContainerInner">
53
- <div className="chatMessageContainerInnerDiv">
54
- <button className="chatMessageContainerInnerDiv_button">
55
- {/* <CaretLeft size={25} /> */}
56
- </button>
57
- <img className="chatMessageContainerInnerImg" alt="Profile" src={selectedConversation.participantDetails.idpic}/>
58
- <div className="chatMessageContainerOutter">
59
- <div className="chatMessageContainerOutterDiv">
60
- <p className="chatMessageContainerOutterDiv_name">
61
- {selectedConversation.participantDetails.firstname}
62
- </p>
63
- <p className="text-sm text-[#12bbb5]">
64
- {isUserOnline ? "Online" : "Offline"}
65
- </p>
66
- </div>
67
- </div>
68
- {/* <h4 className=" inline-block py-2 text-left font-sans font-semibold normal-case">Lara Abegnale</h4> */}
69
- </div>
70
- </div>
48
+ return (
49
+ <div className="chatMessageContainer">
50
+ {!selectedConversation ? (
51
+ <EmptyInbox />
52
+ ) : (
53
+ <>
54
+ <div className="chatMessageContainerInner">
55
+ <div className="chatMessageContainerInnerDiv">
56
+ <button className="chatMessageContainerInnerDiv_button">
57
+ {/* <CaretLeft size={25} /> */}
58
+ </button>
59
+ <img
60
+ className="chatMessageContainerInnerImg"
61
+ alt="Profile"
62
+ src={
63
+ selectedConversation.participantDetails?.profilePic ||
64
+ selectedConversation.participantDetails?.idpic
65
+ }
66
+ />
67
+ <div className="chatMessageContainerOutter">
68
+ <div className="chatMessageContainerOutterDiv">
69
+ <p className="chatMessageContainerOutterDiv_name">
70
+ {selectedConversation.participantDetails.firstname}
71
+ </p>
72
+ <p className="text-sm ">
73
+ {isUserOnline ? "Online" : "Offline"}
74
+ </p>
75
+ </div>
76
+ </div>
77
+ {/* <h4 className=" inline-block py-2 text-left font-sans font-semibold normal-case">Lara Abegnale</h4> */}
78
+ </div>
79
+ </div>
71
80
 
72
- {/* <div className="h-14 overflow-x-hidden">
81
+ {/* <div className="h-14 overflow-x-hidden">
73
82
  <div className="top-0 h-14 px-4 py-4 w-full border-b border-gray-300 justify-start items-start gap-2 inline-flex sticky z-10">
74
83
  <div className="grow shrink basis-0 self-stretch py-2 justify-start items-center gap-4 flex">
75
84
  <button onClick={() => setSelectedConversation(null)} className="text-blue-500 md:hidden">
@@ -87,96 +96,106 @@ const MessageContainer = () => {
87
96
  </div>
88
97
  </div> */}
89
98
 
90
- <Messages />
91
- <MessageInput />
92
- </>
93
- )}
94
- </div>
95
- );
96
- }
99
+ <Messages />
100
+ <MessageInput />
101
+ </>
102
+ )}
103
+ </div>
104
+ );
105
+ };
97
106
 
98
107
  export default MessageContainer;
99
108
 
100
109
  interface EmptyInboxProps {
101
- title?: string;
102
- description?: string;
103
- }
104
-
105
- const EmptyInbox: React.FC<EmptyInboxProps> = ({
106
- title = "Ah, a fresh new inbox",
107
- description = "You haven't started any conversations yet, but when you do, you'll find them here.",
108
- }) => {
109
- return (
110
- <div className="flex flex-col items-center justify-center h-full p-6 text-center">
111
- <div className="w-48 h-48 mb-6 relative">
112
- <svg
113
- viewBox="0 0 200 200"
110
+ title?: string;
111
+ description?: string;
112
+ }
113
+
114
+ const EmptyInbox: React.FC<EmptyInboxProps> = ({
115
+ title = "Ah, a fresh new inbox",
116
+ description = "You haven't started any conversations yet, but when you do, you'll find them here.",
117
+ }) => {
118
+ return (
119
+ <div className="flex flex-col items-center justify-center h-full p-6 text-center">
120
+ <div className="w-48 h-48 mb-6 relative">
121
+ <svg
122
+ viewBox="0 0 200 200"
123
+ fill="none"
124
+ xmlns="http://www.w3.org/2000/svg"
125
+ className="w-full h-full"
126
+ >
127
+ <line
128
+ x1="100"
129
+ y1="100"
130
+ x2="100"
131
+ y2="160"
132
+ stroke="black"
133
+ strokeWidth="2"
134
+ />
135
+ <line
136
+ x1="40"
137
+ y1="160"
138
+ x2="160"
139
+ y2="160"
140
+ stroke="black"
141
+ strokeWidth="1"
142
+ />
143
+ <path
144
+ d="M70 160C75 150 80 155 85 160"
145
+ stroke="black"
146
+ strokeWidth="1"
147
+ fill="none"
148
+ />
149
+ <path
150
+ d="M115 160C120 150 125 155 130 160"
151
+ stroke="black"
152
+ strokeWidth="1"
153
+ fill="none"
154
+ />
155
+ <rect
156
+ x="70"
157
+ y="80"
158
+ width="60"
159
+ height="30"
160
+ stroke="black"
161
+ strokeWidth="1"
162
+ fill="white"
163
+ />
164
+ <path
165
+ d="M70 80C70 65 130 65 130 80"
166
+ stroke="black"
167
+ strokeWidth="1"
168
+ fill="none"
169
+ />
170
+ <rect
171
+ x="70"
172
+ y="80"
173
+ width="60"
174
+ height="20"
175
+ stroke="black"
176
+ strokeWidth="1"
177
+ fill="white"
178
+ />
179
+ <path d="M120 90H125V95H120V90Z" fill="#10B981" />
180
+ <path d="M120 90H125V95H120V90Z" stroke="black" strokeWidth="0.5" />
181
+ <path d="M125 92L130 87" stroke="#10B981" strokeWidth="1" />
182
+ <path d="M125 92L130 97" stroke="#10B981" strokeWidth="1" />
183
+ <path
184
+ d="M130 60C140 55 150 65 140 70"
185
+ stroke="black"
186
+ strokeWidth="1"
187
+ strokeDasharray="2"
114
188
  fill="none"
115
- xmlns="http://www.w3.org/2000/svg"
116
- className="w-full h-full"
117
- >
118
- <line x1="100" y1="100" x2="100" y2="160" stroke="black" strokeWidth="2" />
119
- <line x1="40" y1="160" x2="160" y2="160" stroke="black" strokeWidth="1" />
120
- <path
121
- d="M70 160C75 150 80 155 85 160"
122
- stroke="black"
123
- strokeWidth="1"
124
- fill="none"
125
- />
126
- <path
127
- d="M115 160C120 150 125 155 130 160"
128
- stroke="black"
129
- strokeWidth="1"
130
- fill="none"
131
- />
132
- <rect
133
- x="70"
134
- y="80"
135
- width="60"
136
- height="30"
137
- stroke="black"
138
- strokeWidth="1"
139
- fill="white"
140
- />
141
- <path
142
- d="M70 80C70 65 130 65 130 80"
143
- stroke="black"
144
- strokeWidth="1"
145
- fill="none"
146
- />
147
- <rect
148
- x="70"
149
- y="80"
150
- width="60"
151
- height="20"
152
- stroke="black"
153
- strokeWidth="1"
154
- fill="white"
155
- />
156
- <path d="M120 90H125V95H120V90Z" fill="#10B981" />
157
- <path
158
- d="M120 90H125V95H120V90Z"
159
- stroke="black"
160
- strokeWidth="0.5"
161
- />
162
- <path d="M125 92L130 87" stroke="#10B981" strokeWidth="1" />
163
- <path d="M125 92L130 97" stroke="#10B981" strokeWidth="1" />
164
- <path
165
- d="M130 60C140 55 150 65 140 70"
166
- stroke="black"
167
- strokeWidth="1"
168
- strokeDasharray="2"
169
- fill="none"
170
- />
171
- <text x="140" y="60" fontSize="12" fill="black">
172
-
173
- </text>
174
- <circle cx="85" cy="175" r="5" fill="#10B981" />
175
- <circle cx="115" cy="175" r="5" fill="#10B981" />
176
- </svg>
177
- </div>
178
- <h3 className="text-xl font-medium text-gray-800 mb-2">{title}</h3>
179
- <p className="text-gray-500 max-w-sm">{description}</p>
189
+ />
190
+ <text x="140" y="60" fontSize="12" fill="black">
191
+
192
+ </text>
193
+ <circle cx="85" cy="175" r="5" fill="#10B981" />
194
+ <circle cx="115" cy="175" r="5" fill="#10B981" />
195
+ </svg>
180
196
  </div>
181
- );
182
- };
197
+ <h3 className="text-xl font-medium text-gray-800 mb-2">{title}</h3>
198
+ <p className="text-gray-500 max-w-sm">{description}</p>
199
+ </div>
200
+ );
201
+ };
@@ -9,7 +9,9 @@ import paperplane from "../../assets/icons8-send-50.png";
9
9
  import { FilePreview, FileType } from "../common/FilePreview";
10
10
  import { getApiClient } from "../../lib/api/apiClient";
11
11
  import { MessageStatus } from "../../types/type";
12
- const MAX_FILE_SIZE_MB = 25; // 10MB max file size
12
+ import { Path } from "../../lib/api/endpoint";
13
+ const MAX_FILE_SIZE_MB = 5; // 5MB max file size
14
+ const MAX_FILE_COUNT = 5;
13
15
  const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp','video/mp4', 'video/webm', 'video/ogg'];
14
16
  const ACCEPTED_VIDEO_TYPES = ['video/mp4', 'video/webm', 'video/ogg'];
15
17
  const ACCEPTED_DOCUMENT_TYPES = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/plain'];
@@ -28,6 +30,7 @@ const MessageInput = () => {
28
30
  const { userId } = useChatContext();
29
31
  const { selectedConversation,setMessages } = useChatUIStore();
30
32
  const [message, setMessage] = useState("");
33
+ const [message1, setMessage1] = useState("");
31
34
  const mutation = useMessageMutation();
32
35
  const [typingUser, setTypingUser] = useState<string | null>(null);
33
36
  const [isSending, setIsSending] = useState(false);
@@ -39,7 +42,7 @@ const MessageInput = () => {
39
42
  const attachmentsRef = useRef<Attachment[]>([]);
40
43
  const [tempMessageId, setTempMessageId] = useState<string | null>(null);
41
44
  const [inputError, setInputError] = useState<string | null>(null);
42
-
45
+ const attachmentsContainerRef = useRef<HTMLDivElement>(null);
43
46
  const generateTempId = () => `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
44
47
 
45
48
  // Function to auto-resize the textarea
@@ -108,6 +111,8 @@ const MessageInput = () => {
108
111
  } else {
109
112
  setInputError(null);
110
113
  setMessage(newValue);
114
+ setMessage1(newValue);
115
+
111
116
  }
112
117
  };
113
118
 
@@ -144,7 +149,7 @@ const MessageInput = () => {
144
149
  };
145
150
 
146
151
  const uploadToS3 = async (file: File, onProgress?:(progress:number)=> void): Promise<{ url: string, name: string, size: number, type: FileType }> => {
147
- const response = await apiClient.post('api/chat/generatePresignedUrl', {
152
+ const response = await apiClient.post(`${Path.preSignUrl}`, {
148
153
  fileName: file.name,
149
154
  fileType: file.type,
150
155
  }
@@ -207,14 +212,15 @@ const MessageInput = () => {
207
212
  e.preventDefault();
208
213
  if (!message && attachmentsRef.current.length === 0 || isSending) return;
209
214
  setIsSending(true);
210
-
215
+ setAttachments([]);
216
+ setMessage("");
211
217
  const tempId = generateTempId();
212
218
  setTempMessageId(tempId);
213
219
 
214
220
  const optimisticMessage = {
215
221
  _id: tempId,
216
- text: message, // Added text property to match the expected type
217
- message: message,
222
+ text: message1, // Added text property to match the expected type
223
+ message: message1,
218
224
  senderId: userId,
219
225
  status: 'sending' as MessageStatus,
220
226
  createdAt: new Date().toISOString(),
@@ -289,7 +295,7 @@ const MessageInput = () => {
289
295
  mutation.mutate({
290
296
  chatId: selectedConversation?.participantDetails._id,
291
297
  senderId: userId,
292
- message,
298
+ message: message1,
293
299
  attachments: successfulUploads,
294
300
  }, {
295
301
  onSuccess: (data) => {
@@ -297,7 +303,7 @@ const MessageInput = () => {
297
303
 
298
304
  socket.emit("sendMessage", {
299
305
  chatId: selectedConversation?._id,
300
- message,
306
+ message: message1,
301
307
  messageId: data[1]._id,
302
308
  attachments: successfulUploads,
303
309
  senderId: userId,
@@ -333,7 +339,7 @@ const MessageInput = () => {
333
339
  ));
334
340
  } finally {
335
341
  setIsSending(false);
336
- setMessage("");
342
+ setMessage1("");
337
343
  setAttachments([]);
338
344
  setTempMessageId(null);
339
345
  }
@@ -383,6 +389,22 @@ const MessageInput = () => {
383
389
  const files = e.target.files;
384
390
  if (!files || files.length === 0) return;
385
391
 
392
+ // Check if adding these files would exceed the maximum count
393
+ if (attachments.length + files.length > MAX_FILE_COUNT) {
394
+ alert(`You can only attach up to ${MAX_FILE_COUNT} files`);
395
+ return;
396
+ }
397
+
398
+ // Calculate total size of new files
399
+ const newFilesSize = Array.from(files).reduce((total, file) => total + file.size, 0);
400
+ const currentAttachmentsSize = attachments.reduce((total, att) => total + att.file.size, 0);
401
+
402
+ // Check if total size would exceed the limit
403
+ if (currentAttachmentsSize + newFilesSize > MAX_FILE_SIZE_MB * 1024 * 1024) {
404
+ alert(`Total file size cannot exceed ${MAX_FILE_SIZE_MB}MB`);
405
+ return;
406
+ }
407
+
386
408
  const newAttachments: Attachment[] = [];
387
409
 
388
410
  for (let i = 0; i < files.length; i++) {
@@ -399,7 +421,6 @@ const MessageInput = () => {
399
421
  continue;
400
422
  }
401
423
 
402
-
403
424
  const previewUrl = fileType === 'document'
404
425
  ? URL.createObjectURL(new Blob([''], { type: 'application/pdf' })) // Placeholder for documents
405
426
  : URL.createObjectURL(file);
@@ -417,6 +438,15 @@ const MessageInput = () => {
417
438
  }
418
439
  };
419
440
 
441
+ const scrollAttachments = (direction: 'left' | 'right') => {
442
+ if (attachmentsContainerRef.current) {
443
+ const scrollAmount = direction === 'right' ? 200 : -200;
444
+ attachmentsContainerRef.current.scrollBy({
445
+ left: scrollAmount,
446
+ behavior: 'smooth'
447
+ });
448
+ }
449
+ };
420
450
 
421
451
  const removeAttachment = (index: number) => {
422
452
  setAttachments(prev => {
@@ -431,7 +461,16 @@ const MessageInput = () => {
431
461
  <div className="message-input-container">
432
462
  {/* Preview area for attachments */}
433
463
  {attachments.length > 0 && (
434
- <div className="attachments-preview">
464
+ <div className="attachments-preview-container">
465
+ <button
466
+ className="scroll-button left"
467
+ onClick={() => scrollAttachments('left')}
468
+ disabled={attachments.length <= 3}
469
+ >
470
+ &lt;
471
+ </button>
472
+
473
+ <div className="attachments-preview" ref={attachmentsContainerRef}>
435
474
  {attachments.map((attachment, index) => (
436
475
  <FilePreview
437
476
  key={index}
@@ -441,6 +480,22 @@ const MessageInput = () => {
441
480
  onRemove={() => removeAttachment(index)}
442
481
  />
443
482
  ))}
483
+
484
+ {attachments.length < MAX_FILE_COUNT && (
485
+ <div className="add-more-files" onClick={() => fileInputRef.current?.click()}>
486
+ <div className="plus-icon">+</div>
487
+ <div className="add-more-text">Add more</div>
488
+ </div>
489
+ )}
490
+ </div>
491
+
492
+ <button
493
+ className="scroll-button right"
494
+ onClick={() => scrollAttachments('right')}
495
+ disabled={attachments.length <= 3}
496
+ >
497
+ &gt;
498
+ </button>
444
499
  </div>
445
500
  )}
446
501
 
@@ -16,17 +16,15 @@ const Messages = () => {
16
16
 
17
17
  const lastMessageRef = useRef<HTMLDivElement>(null);
18
18
 
19
- const { data, fetchNextPage, isFetchingNextPage } =
20
- useInfiniteQuery({
21
- queryKey: ["messages", selectedConversation?._id, userId],
22
- queryFn: ({ pageParam = 1 }) =>
23
- fetchMessages(selectedConversation?._id, userId, pageParam),
24
- getNextPageParam: (lastPage) => {
25
- return lastPage.nextPage; // Use the nextPage from API response
26
- },
27
- initialPageParam: 1,
28
- })
29
-
19
+ const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({
20
+ queryKey: ["messages", selectedConversation?._id, userId],
21
+ queryFn: ({ pageParam = 1 }) =>
22
+ fetchMessages(selectedConversation?._id, userId, pageParam),
23
+ getNextPageParam: (lastPage) => {
24
+ return lastPage.nextPage; // Use the nextPage from API response
25
+ },
26
+ initialPageParam: 1,
27
+ });
30
28
 
31
29
  useEffect(() => {
32
30
  if (inView) {
@@ -34,12 +32,11 @@ const Messages = () => {
34
32
  }
35
33
  }, [fetchNextPage, inView]);
36
34
 
37
-
38
35
  useEffect(() => {
39
36
  if (data) {
40
- console.log('message fetching data', data);
37
+ console.log("message fetching data", data);
41
38
 
42
- const allMessages = data.pages.flatMap(page => page.messages).reverse();
39
+ const allMessages = data.pages.flatMap((page) => page.messages).reverse();
43
40
  setMessages(allMessages);
44
41
  }
45
42
  }, [data]);
@@ -52,19 +49,22 @@ const Messages = () => {
52
49
  newMessage.shouldShake = true;
53
50
  console.log("📩 New message received:", newMessage);
54
51
  // setMessages((prevMessages) => [...prevMessages, newMessage[1]]);
55
- setMessages(prevMessages => {
56
- console.log("prevMessages", prevMessages);
57
-
58
- const isDuplicate = prevMessages.some(msg =>
59
- msg._id === newMessage[1]._id ||
60
- (msg.isOptimistic && msg.senderId === userId && msg.message === newMessage[1].message)
52
+ setMessages((prevMessages) => {
53
+ console.log("prevMessages", prevMessages);
54
+
55
+ const isDuplicate = prevMessages.some(
56
+ (msg) =>
57
+ msg._id === newMessage[1]._id ||
58
+ (msg.isOptimistic &&
59
+ msg.senderId === userId &&
60
+ msg.message === newMessage[1].message)
61
61
  );
62
- console.log('isDuplicate', isDuplicate);
63
-
62
+ console.log("isDuplicate", isDuplicate);
63
+
64
64
  if (isDuplicate) {
65
65
  return prevMessages;
66
66
  }
67
-
67
+
68
68
  return [...prevMessages, newMessage[1]];
69
69
  });
70
70
  };
@@ -78,7 +78,6 @@ const Messages = () => {
78
78
  // });
79
79
  // };
80
80
 
81
-
82
81
  const handleStatusUpdate = ({
83
82
  messageId,
84
83
  status,
@@ -157,17 +156,14 @@ const Messages = () => {
157
156
  // }
158
157
  // }, [handleScroll]);
159
158
 
160
-
161
159
  console.log("📩 Messages:", messages);
162
160
  console.log("📩 Messages Length:", messages?.length);
163
161
 
164
-
165
-
166
162
  return (
167
- <div className="chatMessages"
168
- style={{ overflowY: 'auto', height: '100%', position: 'relative' }}
163
+ <div
164
+ className="chatMessages"
165
+ style={{ overflowY: "auto", height: "100%", position: "relative" }}
169
166
  >
170
-
171
167
  <div ref={ref} className="my-8">
172
168
  {isFetchingNextPage ? <Loader /> : null}
173
169
  </div>
@@ -175,8 +171,10 @@ const Messages = () => {
175
171
  messages?.map((message: any) =>
176
172
  // Check if the message object is valid and has an _id before rendering
177
173
  message ? (
178
- <div key={message._id} ref={lastMessageRef}
179
- style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}
174
+ <div
175
+ key={message._id}
176
+ ref={lastMessageRef}
177
+ style={{ flex: 1, minHeight: 0, overflowY: "auto" }}
180
178
  >
181
179
  <Message message={message} />
182
180
  </div>
@@ -187,7 +185,6 @@ const Messages = () => {
187
185
  Send a message to start the conversation
188
186
  </p>
189
187
  )}
190
-
191
188
  </div>
192
189
  );
193
190
  };
@@ -47,14 +47,24 @@ const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
47
47
  onlineUsers?.includes(conversation.participantDetails._id);
48
48
  return (
49
49
  <>
50
- <div className="conversation-container" onClick={handleSelectConversation}>
50
+ <div
51
+ className="conversation-container"
52
+ onClick={handleSelectConversation}
53
+ >
51
54
  <div className="conversation-avatar">
52
55
  <img
53
56
  className="conversation-img"
54
- src={conversation.participantDetails?.profilePic || conversation.participantDetails?.idpic}
57
+ src={
58
+ conversation.participantDetails?.profilePic ||
59
+ conversation.participantDetails?.idpic
60
+ }
55
61
  alt="User Avatar"
56
62
  />
57
- <span className={`chatSidebarStatusDot ${isUserOnline ? "online" : "offline"}`}></span>
63
+ <span
64
+ className={`chatSidebarStatusDot ${
65
+ isUserOnline ? "online" : "offline"
66
+ }`}
67
+ ></span>
58
68
  </div>
59
69
 
60
70
  <div className="conversation-info">
@@ -63,10 +73,13 @@ const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
63
73
  {conversation.participantDetails?.firstname}
64
74
  </p>
65
75
  <span className="conversation-time">
66
- {new Date(conversation.lastMessage.createdAt).toLocaleTimeString([], {
67
- hour: "2-digit",
68
- minute: "2-digit",
69
- })}
76
+ {new Date(conversation.lastMessage.createdAt).toLocaleTimeString(
77
+ [],
78
+ {
79
+ hour: "2-digit",
80
+ minute: "2-digit",
81
+ }
82
+ )}
70
83
  </span>
71
84
  </div>
72
85
  <p className="conversation-message">