@realtimexsco/live-chat 1.0.0 → 1.1.0

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/dist/index.mjs CHANGED
@@ -3,13 +3,14 @@ import axios from 'axios';
3
3
  import { create } from 'zustand';
4
4
  import { persist } from 'zustand/middleware';
5
5
  import * as React7 from 'react';
6
- import React7__default, { createContext, useContext, useState, useEffect, useRef, useMemo, useLayoutEffect, useCallback } from 'react';
6
+ import React7__default, { createContext, useContext, useState, useEffect, useRef, useMemo, useLayoutEffect, useCallback, useSyncExternalStore } from 'react';
7
7
  import { Tooltip as Tooltip$1, Avatar as Avatar$1, Dialog as Dialog$1, Slot, ScrollArea as ScrollArea$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, AlertDialog as AlertDialog$1, Switch as Switch$1 } from 'radix-ui';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import { Search, X, UserPlus, MessageCircle, Users, Camera, Check, ArrowRight, Loader2, MessageCircleMore, History, Paperclip, Smile, Send, XIcon, ArrowLeft, Pin, Star, Phone, Video, MoreVertical, Info, UserKey, ShieldUser, MessageSquareX, Trash2, LogOut, Ban, AlertCircle, Save, Calendar, Mail, User, ShieldCheck, UserMinus, Plus, Download, Forward, Music, Film, FileText, FileSpreadsheet, FileIcon, PinOff, StarOff, CornerUpLeft, Pencil, Clock, CheckCheck } from 'lucide-react';
11
11
  import { cva } from 'class-variance-authority';
12
12
  import EmojiPicker, { Theme } from 'emoji-picker-react';
13
+ import { toast } from 'react-hot-toast';
13
14
 
14
15
  var __defProp = Object.defineProperty;
15
16
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -142,11 +143,28 @@ var init_socket_events_constants = __esm({
142
143
  };
143
144
  }
144
145
  });
145
- var SocketService, socketService, socket_service_default;
146
+
147
+ // src/store/useSocketStore.ts
148
+ var useSocketStore_exports = {};
149
+ __export(useSocketStore_exports, {
150
+ default: () => useSocketStore_default,
151
+ useSocketStore: () => useSocketStore
152
+ });
153
+ var useSocketStore, useSocketStore_default;
154
+ var init_useSocketStore = __esm({
155
+ "src/store/useSocketStore.ts"() {
156
+ init_useStore();
157
+ useSocketStore = useStore;
158
+ useSocketStore_default = useSocketStore;
159
+ }
160
+ });
161
+ var getStore, SocketService, socketService, socket_service_default;
146
162
  var init_socket_service = __esm({
147
163
  "src/services/socket.service.ts"() {
148
164
  init_socket_events_constants();
149
- init_useStore();
165
+ getStore = () => {
166
+ return (init_useSocketStore(), __toCommonJS(useSocketStore_exports)).useSocketStore;
167
+ };
150
168
  SocketService = class {
151
169
  constructor() {
152
170
  this.socket = null;
@@ -165,7 +183,7 @@ var init_socket_service = __esm({
165
183
  }
166
184
  this.currentConfig = config;
167
185
  this.isConnecting = true;
168
- const { setConnecting, setConnected, setDisconnected, setError } = useStore_default.getState();
186
+ const { setConnecting, setConnected, setDisconnected, setError } = getStore().getState();
169
187
  setConnecting();
170
188
  this.socket = io(config.url, config.options);
171
189
  this.socket.on("connect", () => {
@@ -183,72 +201,72 @@ var init_socket_service = __esm({
183
201
  });
184
202
  this.socket.on(SOCKET_EVENTS.ERROR, (payload) => {
185
203
  const message = typeof payload === "string" ? payload : payload?.message ?? "app_error";
186
- useStore_default.getState().handleAppError(message);
204
+ getStore().getState().handleAppError(message);
187
205
  });
188
206
  this.socket.on(SOCKET_EVENTS.DM_RECEIVE, (payload) => {
189
- useStore_default.getState().receivedDM(payload);
207
+ getStore().getState().receivedDM(payload);
190
208
  });
191
209
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_RECEIVE, (payload) => {
192
- useStore_default.getState().receivedGroupMessage(payload);
210
+ getStore().getState().receivedGroupMessage(payload);
193
211
  });
194
212
  this.socket.on(SOCKET_EVENTS.CONVERSATION_UPDATE, (payload) => {
195
213
  if (!payload) return;
196
- useStore_default.getState().updateConversation(payload);
214
+ getStore().getState().updateConversation(payload);
197
215
  });
198
216
  this.socket.on(SOCKET_EVENTS.DM_EDIT_RECEIVE, (payload) => {
199
- useStore_default.getState().receivedEditDM(payload);
217
+ getStore().getState().receivedEditDM(payload);
200
218
  });
201
219
  this.socket.on(SOCKET_EVENTS.DM_DELETE_ME, (payload) => {
202
- useStore_default.getState().receivedDeleteForMe(payload);
220
+ getStore().getState().receivedDeleteForMe(payload);
203
221
  });
204
222
  this.socket.on(SOCKET_EVENTS.DM_DELETE_EVERYONE, (payload) => {
205
- useStore_default.getState().receivedDeleteForEveryone(payload);
223
+ getStore().getState().receivedDeleteForEveryone(payload);
206
224
  });
207
225
  this.socket.on(SOCKET_EVENTS.DM_CLEAR_CHAT_SUCCESS, (payload) => {
208
- useStore_default.getState().receivedDmClearChat(payload);
226
+ getStore().getState().receivedDmClearChat(payload);
209
227
  });
210
228
  this.socket.on(SOCKET_EVENTS.GROUP_CLEAR_CHAT_SUCCESS, (payload) => {
211
- useStore_default.getState().receivedGroupClearChat(payload);
229
+ getStore().getState().receivedGroupClearChat(payload);
212
230
  });
213
231
  this.socket.on(SOCKET_EVENTS.DM_DELETE_CONVERSATION_SUCCESS, (payload) => {
214
- useStore_default.getState().receivedDmDeleteConversation(payload);
232
+ getStore().getState().receivedDmDeleteConversation(payload);
215
233
  });
216
234
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION_SUCCESS, (payload) => {
217
- useStore_default.getState().receivedGroupDeleteConversation(payload);
235
+ getStore().getState().receivedGroupDeleteConversation(payload);
218
236
  });
219
237
  this.socket.on(SOCKET_EVENTS.DM_REACTION_ADD_RECEIVE, (payload) => {
220
- useStore_default.getState().receivedReactionAdd(payload);
238
+ getStore().getState().receivedReactionAdd(payload);
221
239
  });
222
240
  this.socket.on(SOCKET_EVENTS.DM_REACTION_REMOVE_RECEIVE, (payload) => {
223
- useStore_default.getState().receivedReactionRemove(payload);
241
+ getStore().getState().receivedReactionRemove(payload);
224
242
  });
225
243
  this.socket.on(SOCKET_EVENTS.DM_PIN_UPDATE, (payload) => {
226
- useStore_default.getState().receivedPinUpdate(payload);
244
+ getStore().getState().receivedPinUpdate(payload);
227
245
  });
228
246
  this.socket.on(SOCKET_EVENTS.DM_STAR_UPDATE, (payload) => {
229
- useStore_default.getState().receivedStarUpdate(payload);
247
+ getStore().getState().receivedStarUpdate(payload);
230
248
  });
231
249
  this.socket.on(SOCKET_EVENTS.DM_STATUS_UPDATE, (payload) => {
232
- useStore_default.getState().receivedStatusUpdate(payload);
250
+ getStore().getState().receivedStatusUpdate(payload);
233
251
  });
234
252
  this.socket.on(SOCKET_EVENTS.DM_MARK_READ, (payload) => {
235
- useStore_default.getState().receivedDMMarkRead(payload);
253
+ getStore().getState().receivedDMMarkRead(payload);
236
254
  });
237
255
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_READ, (payload) => {
238
- useStore_default.getState().receivedGroupMarkRead(payload);
256
+ getStore().getState().receivedGroupMarkRead(payload);
239
257
  });
240
258
  this.socket.on(SOCKET_EVENTS.DM_FORWARD_RECEIVE, (payload) => {
241
- useStore_default.getState().receivedForward(payload);
259
+ getStore().getState().receivedForward(payload);
242
260
  });
243
261
  this.socket.on(SOCKET_EVENTS.DM_TYPING_START, (payload) => {
244
- useStore_default.getState().setTypingStart(payload);
262
+ getStore().getState().setTypingStart(payload);
245
263
  });
246
264
  this.socket.on(SOCKET_EVENTS.DM_TYPING_STOP, (payload) => {
247
- useStore_default.getState().setTypingStop(payload);
265
+ getStore().getState().setTypingStop(payload);
248
266
  });
249
267
  this.socket.on(SOCKET_EVENTS.ONLINE_USERS_LIST, (payload) => {
250
268
  const users = payload?.onlineUsers || [];
251
- useStore_default.getState().setOnlineUsers(users);
269
+ getStore().getState().setOnlineUsers(users);
252
270
  });
253
271
  this.socket.on(SOCKET_EVENTS.USER_ONLINE, () => {
254
272
  this.emitGetOnlineUsers();
@@ -257,49 +275,49 @@ var init_socket_service = __esm({
257
275
  this.emitGetOnlineUsers();
258
276
  });
259
277
  this.socket.on(SOCKET_EVENTS.CONVERSATION_STARRED_PINNED_COUNT_UPDATE, (payload) => {
260
- useStore_default.getState().receivedCountUpdate(payload);
278
+ getStore().getState().receivedCountUpdate(payload);
261
279
  });
262
280
  this.socket.on(SOCKET_EVENTS.GROUP_EDIT_RECEIVE, (payload) => {
263
- useStore_default.getState().receivedEditDM(payload);
281
+ getStore().getState().receivedEditDM(payload);
264
282
  });
265
283
  this.socket.on(SOCKET_EVENTS.GROUP_STATUS_UPDATE, (payload) => {
266
- useStore_default.getState().receivedGroupStatusUpdate(payload);
284
+ getStore().getState().receivedGroupStatusUpdate(payload);
267
285
  });
268
286
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_DELIVERED, (payload) => {
269
- useStore_default.getState().receivedGroupStatusUpdate({
287
+ getStore().getState().receivedGroupStatusUpdate({
270
288
  ...payload,
271
289
  status: payload?.status || "delivered"
272
290
  });
273
291
  });
274
292
  this.socket.on(SOCKET_EVENTS.GROUP_REACTION_ADD_RECEIVE, (payload) => {
275
- useStore_default.getState().receivedGroupReactionAdd(payload);
293
+ getStore().getState().receivedGroupReactionAdd(payload);
276
294
  });
277
295
  this.socket.on(SOCKET_EVENTS.GROUP_TYPING_START, (payload) => {
278
- useStore_default.getState().setTypingStart(payload);
296
+ getStore().getState().setTypingStart(payload);
279
297
  });
280
298
  this.socket.on(SOCKET_EVENTS.GROUP_TYPING_STOP, (payload) => {
281
- useStore_default.getState().setTypingStop(payload);
299
+ getStore().getState().setTypingStop(payload);
282
300
  });
283
301
  this.socket.on(SOCKET_EVENTS.GROUP_REACTION_REMOVE_RECEIVE, (payload) => {
284
- useStore_default.getState().receivedGroupReactionRemove(payload);
302
+ getStore().getState().receivedGroupReactionRemove(payload);
285
303
  });
286
304
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_ME, (payload) => {
287
- useStore_default.getState().receivedDeleteForMe(payload);
305
+ getStore().getState().receivedDeleteForMe(payload);
288
306
  });
289
307
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_EVERYONE, (payload) => {
290
- useStore_default.getState().receivedDeleteForEveryone(payload);
308
+ getStore().getState().receivedDeleteForEveryone(payload);
291
309
  });
292
310
  this.socket.on(SOCKET_EVENTS.GROUP_PIN_UPDATE, (payload) => {
293
- useStore_default.getState().receivedPinUpdate(payload);
311
+ getStore().getState().receivedPinUpdate(payload);
294
312
  });
295
313
  this.socket.on(SOCKET_EVENTS.GROUP_STAR_UPDATE, (payload) => {
296
- useStore_default.getState().receivedStarUpdate(payload);
314
+ getStore().getState().receivedStarUpdate(payload);
297
315
  });
298
316
  this.socket.on(SOCKET_EVENTS.GROUP_FORWARD_RECEIVE, (payload) => {
299
- useStore_default.getState().receivedForward(payload);
317
+ getStore().getState().receivedForward(payload);
300
318
  });
301
319
  this.socket.on(SOCKET_EVENTS.GROUP_LEAVE_RECEIVE, (payload) => {
302
- useStore_default.getState().receivedGroupLeave(payload);
320
+ getStore().getState().receivedGroupLeave(payload);
303
321
  });
304
322
  }
305
323
  disconnect() {
@@ -429,6 +447,176 @@ var init_socket_service = __esm({
429
447
  }
430
448
  });
431
449
 
450
+ // src/store/helpers/store.helpers.ts
451
+ var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, mergeLastMessage, getLatestMessageFromList, resolveConversationLastMessage, syncConversationLastMessage, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
452
+ var init_store_helpers = __esm({
453
+ "src/store/helpers/store.helpers.ts"() {
454
+ findConversationIdByMessageId = (messagesByConversation, messageId) => {
455
+ return Object.keys(messagesByConversation).find(
456
+ (cid) => messagesByConversation[cid].some(
457
+ (m) => String(m._id || m.id) === String(messageId)
458
+ )
459
+ );
460
+ };
461
+ normalizeId = (id) => String(id?._id || id?.id || id || "");
462
+ normalizeUnreadCount = (unreadCount) => {
463
+ if (typeof unreadCount === "number") return unreadCount;
464
+ return 0;
465
+ };
466
+ incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
467
+ emptyMessagesMetadata = () => ({
468
+ nextCursor: null,
469
+ hasMore: false,
470
+ isFirstPage: true
471
+ });
472
+ extractMessageData = (payload) => {
473
+ const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
474
+ const content = payload.content || payload.message?.content;
475
+ const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
476
+ const status = payload.status || payload.message?.status;
477
+ const isPinned = payload.isPinned ?? payload.message?.isPinned;
478
+ const isStarred = payload.isStarred ?? payload.message?.isStarred;
479
+ return { messageId, content, conversationId, status, isPinned, isStarred };
480
+ };
481
+ mergeLastMessage = (existing, incoming) => {
482
+ if (!incoming) return existing ?? null;
483
+ if (!existing) return incoming;
484
+ const content = incoming.content?.trim() ? incoming.content : existing.content;
485
+ const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
486
+ return {
487
+ ...existing,
488
+ ...incoming,
489
+ ...content !== void 0 ? { content } : {},
490
+ ...createdAt !== void 0 ? { createdAt } : {}
491
+ };
492
+ };
493
+ getLatestMessageFromList = (messages) => {
494
+ if (!messages?.length) return null;
495
+ return messages.reduce((latest, message) => {
496
+ const latestTime = new Date(latest.createdAt || latest.timestamp || 0).getTime();
497
+ const messageTime = new Date(message.createdAt || message.timestamp || 0).getTime();
498
+ return messageTime >= latestTime ? message : latest;
499
+ });
500
+ };
501
+ resolveConversationLastMessage = (conversation, messagesByConversation) => {
502
+ const conversationId = String(conversation._id || conversation.id || "");
503
+ const storedLast = conversation.lastMessage;
504
+ if (storedLast?.content?.trim()) return storedLast;
505
+ const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
506
+ return mergeLastMessage(storedLast, latestFromMessages);
507
+ };
508
+ syncConversationLastMessage = (conversations, conversationId, messages) => {
509
+ const latest = getLatestMessageFromList(messages);
510
+ if (!latest) return conversations;
511
+ return conversations.map((conversation) => {
512
+ if (String(conversation._id) !== String(conversationId)) return conversation;
513
+ return {
514
+ ...conversation,
515
+ lastMessage: mergeLastMessage(conversation.lastMessage, latest)
516
+ };
517
+ });
518
+ };
519
+ isGroupConversation = (conversation) => {
520
+ if (!conversation) return false;
521
+ return conversation.conversationType === "group" || !!conversation.isGroup;
522
+ };
523
+ findConversationById = (conversations, conversationId) => {
524
+ if (!conversationId) return void 0;
525
+ return conversations.find((c) => String(c._id) === String(conversationId));
526
+ };
527
+ resolveIsGroupFromPayload = (payload, conversations) => {
528
+ const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
529
+ const fromStore = findConversationById(conversations, conversationId);
530
+ if (fromStore) return isGroupConversation(fromStore);
531
+ return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
532
+ };
533
+ getOtherParticipantId = (conversation, loggedUserId) => {
534
+ if (!conversation?.participants?.length) return "";
535
+ const other = conversation.participants.find(
536
+ (p) => String(p._id || p) !== String(loggedUserId)
537
+ ) || conversation.participants[0];
538
+ return String(other?._id || other || "");
539
+ };
540
+ findConversationForChannel = (channel, loggedUserId, conversations) => {
541
+ if (!channel || !loggedUserId) return void 0;
542
+ if (channel.conversationId) {
543
+ const byId = findConversationById(conversations, channel.conversationId);
544
+ if (byId) return byId;
545
+ }
546
+ if (channel.isGroup) {
547
+ return conversations.find(
548
+ (c) => isGroupConversation(c) && String(c._id) === String(channel.id)
549
+ );
550
+ }
551
+ return conversations.find((c) => {
552
+ if (isGroupConversation(c)) return false;
553
+ const ids = (c.participants || []).map((p) => String(p._id || p));
554
+ return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
555
+ });
556
+ };
557
+ resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
558
+ const found = findConversationForChannel(channel, loggedUserId, conversations);
559
+ if (found?._id) return String(found._id);
560
+ if (channel?.conversationId) return String(channel.conversationId);
561
+ return null;
562
+ };
563
+ getMessageContent = (message) => String(message?.content || "").trim();
564
+ getMessageTimestamp = (message) => {
565
+ const value = message?.createdAt || message?.timestamp;
566
+ const time = value ? new Date(value).getTime() : NaN;
567
+ return Number.isFinite(time) ? time : 0;
568
+ };
569
+ isOwnMessage = (message, loggedUserId) => {
570
+ if (message?.isOwnMessage) return true;
571
+ if (!loggedUserId) return false;
572
+ const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
573
+ return String(senderId) === String(loggedUserId);
574
+ };
575
+ STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
576
+ pickBetterMessageStatus = (current, incoming) => {
577
+ if (!incoming) return current;
578
+ if (!current) return incoming;
579
+ return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
580
+ };
581
+ mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
582
+ const merged = /* @__PURE__ */ new Map();
583
+ apiMessages.forEach((message) => {
584
+ const id = String(message?._id || message?.id || "");
585
+ if (id) merged.set(id, message);
586
+ });
587
+ localMessages.forEach((localMessage) => {
588
+ if (!localMessage) return;
589
+ const localId = String(localMessage._id || localMessage.id || "");
590
+ if (localId && merged.has(localId)) {
591
+ const existing = merged.get(localId);
592
+ merged.set(localId, {
593
+ ...existing,
594
+ ...localMessage,
595
+ status: pickBetterMessageStatus(existing?.status, localMessage?.status)
596
+ });
597
+ return;
598
+ }
599
+ const duplicateInApi = apiMessages.some((apiMessage) => {
600
+ const apiId = String(apiMessage?._id || apiMessage?.id || "");
601
+ if (localId && apiId && localId === apiId) return true;
602
+ if (!isOwnMessage(localMessage, loggedUserId)) return false;
603
+ if (!isOwnMessage(apiMessage, loggedUserId)) return false;
604
+ if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
605
+ return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
606
+ });
607
+ if (duplicateInApi) return;
608
+ const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
609
+ if (!merged.has(key)) merged.set(key, localMessage);
610
+ });
611
+ return Array.from(merged.values()).filter((message, index, list) => {
612
+ const id = String(message?._id || message?.id || "");
613
+ if (!id) return true;
614
+ return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
615
+ });
616
+ };
617
+ }
618
+ });
619
+
432
620
  // src/constants/api.constants.ts
433
621
  var API_ENDPOINTS;
434
622
  var init_api_constants = __esm({
@@ -456,21 +644,6 @@ var init_api_constants = __esm({
456
644
  };
457
645
  }
458
646
  });
459
-
460
- // src/store/useSocketStore.ts
461
- var useSocketStore_exports = {};
462
- __export(useSocketStore_exports, {
463
- default: () => useSocketStore_default,
464
- useSocketStore: () => useSocketStore
465
- });
466
- var useSocketStore, useSocketStore_default;
467
- var init_useSocketStore = __esm({
468
- "src/store/useSocketStore.ts"() {
469
- init_useStore();
470
- useSocketStore = useStore;
471
- useSocketStore_default = useSocketStore;
472
- }
473
- });
474
647
  var chatClientId, chatAccessToken, rateLimitResetAt, setChatClientId, setChatAccessToken, isRateLimited, apiClient, setChatBaseURL, clientUserLogin, fetchUsers, fetchConversations, fetchMessages, fetchPinnedMessages, fetchStarredMessages, createGroup, fetchConversationInfo, updateConversation, updateGroupInformation, addRemoveGroupMembers, addRemoveGroupAdmins, searchMessages, searchMessagesContext, blockUnblockUser;
475
648
  var init_api_service = __esm({
476
649
  "src/services/api.service.ts"() {
@@ -691,138 +864,6 @@ var init_api_service = __esm({
691
864
  }
692
865
  });
693
866
 
694
- // src/store/helpers/store.helpers.ts
695
- var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
696
- var init_store_helpers = __esm({
697
- "src/store/helpers/store.helpers.ts"() {
698
- findConversationIdByMessageId = (messagesByConversation, messageId) => {
699
- return Object.keys(messagesByConversation).find(
700
- (cid) => messagesByConversation[cid].some(
701
- (m) => String(m._id || m.id) === String(messageId)
702
- )
703
- );
704
- };
705
- normalizeId = (id) => String(id?._id || id?.id || id || "");
706
- normalizeUnreadCount = (unreadCount) => {
707
- if (typeof unreadCount === "number") return unreadCount;
708
- return 0;
709
- };
710
- incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
711
- emptyMessagesMetadata = () => ({
712
- nextCursor: null,
713
- hasMore: false,
714
- isFirstPage: true
715
- });
716
- extractMessageData = (payload) => {
717
- const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
718
- const content = payload.content || payload.message?.content;
719
- const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
720
- const status = payload.status || payload.message?.status;
721
- const isPinned = payload.isPinned ?? payload.message?.isPinned;
722
- const isStarred = payload.isStarred ?? payload.message?.isStarred;
723
- return { messageId, content, conversationId, status, isPinned, isStarred };
724
- };
725
- isGroupConversation = (conversation) => {
726
- if (!conversation) return false;
727
- return conversation.conversationType === "group" || !!conversation.isGroup;
728
- };
729
- findConversationById = (conversations, conversationId) => {
730
- if (!conversationId) return void 0;
731
- return conversations.find((c) => String(c._id) === String(conversationId));
732
- };
733
- resolveIsGroupFromPayload = (payload, conversations) => {
734
- const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
735
- const fromStore = findConversationById(conversations, conversationId);
736
- if (fromStore) return isGroupConversation(fromStore);
737
- return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
738
- };
739
- getOtherParticipantId = (conversation, loggedUserId) => {
740
- if (!conversation?.participants?.length) return "";
741
- const other = conversation.participants.find(
742
- (p) => String(p._id || p) !== String(loggedUserId)
743
- ) || conversation.participants[0];
744
- return String(other?._id || other || "");
745
- };
746
- findConversationForChannel = (channel, loggedUserId, conversations) => {
747
- if (!channel || !loggedUserId) return void 0;
748
- if (channel.conversationId) {
749
- const byId = findConversationById(conversations, channel.conversationId);
750
- if (byId) return byId;
751
- }
752
- if (channel.isGroup) {
753
- return conversations.find(
754
- (c) => isGroupConversation(c) && String(c._id) === String(channel.id)
755
- );
756
- }
757
- return conversations.find((c) => {
758
- if (isGroupConversation(c)) return false;
759
- const ids = (c.participants || []).map((p) => String(p._id || p));
760
- return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
761
- });
762
- };
763
- resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
764
- const found = findConversationForChannel(channel, loggedUserId, conversations);
765
- if (found?._id) return String(found._id);
766
- if (channel?.conversationId) return String(channel.conversationId);
767
- return null;
768
- };
769
- getMessageContent = (message) => String(message?.content || "").trim();
770
- getMessageTimestamp = (message) => {
771
- const value = message?.createdAt || message?.timestamp;
772
- const time = value ? new Date(value).getTime() : NaN;
773
- return Number.isFinite(time) ? time : 0;
774
- };
775
- isOwnMessage = (message, loggedUserId) => {
776
- if (message?.isOwnMessage) return true;
777
- if (!loggedUserId) return false;
778
- const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
779
- return String(senderId) === String(loggedUserId);
780
- };
781
- STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
782
- pickBetterMessageStatus = (current, incoming) => {
783
- if (!incoming) return current;
784
- if (!current) return incoming;
785
- return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
786
- };
787
- mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
788
- const merged = /* @__PURE__ */ new Map();
789
- apiMessages.forEach((message) => {
790
- const id = String(message?._id || message?.id || "");
791
- if (id) merged.set(id, message);
792
- });
793
- localMessages.forEach((localMessage) => {
794
- if (!localMessage) return;
795
- const localId = String(localMessage._id || localMessage.id || "");
796
- if (localId && merged.has(localId)) {
797
- const existing = merged.get(localId);
798
- merged.set(localId, {
799
- ...existing,
800
- ...localMessage,
801
- status: pickBetterMessageStatus(existing?.status, localMessage?.status)
802
- });
803
- return;
804
- }
805
- const duplicateInApi = apiMessages.some((apiMessage) => {
806
- const apiId = String(apiMessage?._id || apiMessage?.id || "");
807
- if (localId && apiId && localId === apiId) return true;
808
- if (!isOwnMessage(localMessage, loggedUserId)) return false;
809
- if (!isOwnMessage(apiMessage, loggedUserId)) return false;
810
- if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
811
- return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
812
- });
813
- if (duplicateInApi) return;
814
- const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
815
- if (!merged.has(key)) merged.set(key, localMessage);
816
- });
817
- return Array.from(merged.values()).filter((message, index, list) => {
818
- const id = String(message?._id || message?.id || "");
819
- if (!id) return true;
820
- return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
821
- });
822
- };
823
- }
824
- });
825
-
826
867
  // src/store/forwardPending.store.ts
827
868
  var pendingForwards, lastPendingMessageId, setPendingForward, peekPendingForward, peekMostRecentPendingForward, getLastPendingForwardMessageId, clearPendingForward;
828
869
  var init_forwardPending_store = __esm({
@@ -1470,8 +1511,8 @@ var init_socket_slice = __esm({
1470
1511
  "src/store/slices/socket.slice.ts"() {
1471
1512
  init_socket_service();
1472
1513
  init_socket_events_constants();
1473
- init_api_service();
1474
1514
  init_store_helpers();
1515
+ init_api_service();
1475
1516
  init_forwardPending_store();
1476
1517
  init_message_handlers();
1477
1518
  init_group_handlers();
@@ -1935,6 +1976,7 @@ var init_socket_slice = __esm({
1935
1976
  }));
1936
1977
  },
1937
1978
  // Fetching
1979
+ // Fetching
1938
1980
  fetchConversations: async (limit = 10, page = 1, search = "", force = false) => {
1939
1981
  const fetchKey = `${limit}:${page}:${search}`;
1940
1982
  const state = get();
@@ -1947,7 +1989,15 @@ var init_socket_slice = __esm({
1947
1989
  const response = await fetchConversations(limit, page, search);
1948
1990
  const data = safelyExtractArray(response);
1949
1991
  set((state2) => ({
1950
- conversations: page === 1 ? data : [...state2.conversations, ...data],
1992
+ conversations: page === 1 ? data.map((incoming) => {
1993
+ const existing = state2.conversations.find(
1994
+ (conversation) => String(conversation._id) === String(incoming._id)
1995
+ );
1996
+ return {
1997
+ ...incoming,
1998
+ lastMessage: mergeLastMessage(existing?.lastMessage, incoming.lastMessage)
1999
+ };
2000
+ }) : [...state2.conversations, ...data],
1951
2001
  conversationsHasMore: data.length === limit,
1952
2002
  conversationsPage: page,
1953
2003
  isFetchingConversations: false
@@ -1999,6 +2049,7 @@ var init_socket_slice = __esm({
1999
2049
  isFirstPage: isInitialLoad
2000
2050
  }
2001
2051
  },
2052
+ conversations: isInitialLoad ? syncConversationLastMessage(state2.conversations, conversationId, unique) : state2.conversations,
2002
2053
  isFetchingMessages: false
2003
2054
  };
2004
2055
  });
@@ -2028,6 +2079,7 @@ var init_socket_slice = __esm({
2028
2079
  const tempId = payload.tempId || `temp-${Date.now()}`;
2029
2080
  set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [tempId]: ["send"] } }));
2030
2081
  socket_service_default.emit("dm_send", { ...payload, tempId });
2082
+ socket_service_default.emit("conversation_update", { conversationId: payload.conversationId });
2031
2083
  },
2032
2084
  emitDmEdit: (payload) => {
2033
2085
  set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [payload.messageId]: ["edit"] } }));
@@ -2166,27 +2218,22 @@ var init_socket_slice = __esm({
2166
2218
  const { conversationId } = payload;
2167
2219
  const readerId = payload.userId || payload.readerId || payload.readBy || payload.sender || payload.senderId;
2168
2220
  if (!conversationId) return;
2169
- const readStatus = payload.status || "read";
2170
2221
  set({ lastDMMarkRead: payload });
2171
2222
  set((state) => {
2172
2223
  const messages = state.messagesByConversation[conversationId] || [];
2173
2224
  const loggedUserDetails = get().loggedUserDetails || state.loggedUserDetails;
2174
2225
  const myId = loggedUserDetails?._id || loggedUserDetails?.id;
2175
2226
  const isReaderMe = String(readerId) === String(myId);
2176
- const nextMessages = messages.map((m) => {
2177
- if (isReaderMe) return m;
2178
- const senderId = String(m.sender?._id || m.sender);
2179
- if (senderId === String(myId)) {
2180
- return { ...m, status: readStatus };
2181
- }
2182
- return m;
2183
- });
2227
+ const nextMessages = messages.map((m) => ({ ...m, status: "read" }));
2184
2228
  const nextConversations = state.conversations.map((c) => {
2185
2229
  if (String(c._id) !== String(conversationId)) return c;
2186
2230
  const nextUnreadCount = isReaderMe ? 0 : normalizeUnreadCount(c.unreadCount);
2187
- let nextLastMessage = c.lastMessage;
2188
- if (nextLastMessage && !isReaderMe && String(nextLastMessage.sender?._id || nextLastMessage.sender) === String(myId)) {
2189
- nextLastMessage = { ...nextLastMessage, status: readStatus };
2231
+ let nextLastMessage = mergeLastMessage(c.lastMessage, { status: "read" });
2232
+ if (!nextLastMessage?.content && messages.length) {
2233
+ nextLastMessage = mergeLastMessage(
2234
+ getLatestMessageFromList(messages),
2235
+ { status: "read" }
2236
+ );
2190
2237
  }
2191
2238
  return { ...c, unreadCount: nextUnreadCount, lastMessage: nextLastMessage };
2192
2239
  });
@@ -2348,7 +2395,7 @@ var init_socket_slice = __esm({
2348
2395
  return {
2349
2396
  ...c,
2350
2397
  ...updated,
2351
- lastMessage: updated.lastMessage || c.lastMessage,
2398
+ lastMessage: mergeLastMessage(c.lastMessage, updated.lastMessage),
2352
2399
  // Group conversation_update no longer carries per-user unread; keep local count
2353
2400
  unreadCount: updated.unreadCount !== void 0 ? normalizeUnreadCount(updated.unreadCount) : normalizeUnreadCount(c.unreadCount)
2354
2401
  };
@@ -3755,6 +3802,84 @@ function ScrollBar({
3755
3802
  );
3756
3803
  }
3757
3804
  init_useStore();
3805
+ init_store_helpers();
3806
+ var conversationDrafts = /* @__PURE__ */ new Map();
3807
+ var DRAFT_PREVIEW_MAX_LENGTH = 50;
3808
+ var emptyDraft = () => ({
3809
+ messageInput: "",
3810
+ attachments: []
3811
+ });
3812
+ var draftRevision = 0;
3813
+ var draftListeners = /* @__PURE__ */ new Set();
3814
+ function notifyDraftChange() {
3815
+ draftRevision += 1;
3816
+ draftListeners.forEach((listener) => listener());
3817
+ }
3818
+ function subscribeToDrafts(listener) {
3819
+ draftListeners.add(listener);
3820
+ return () => {
3821
+ draftListeners.delete(listener);
3822
+ };
3823
+ }
3824
+ function getDraftRevision() {
3825
+ return draftRevision;
3826
+ }
3827
+ function useDraftRevision() {
3828
+ return useSyncExternalStore(subscribeToDrafts, getDraftRevision, getDraftRevision);
3829
+ }
3830
+ function getConversationDraft(conversationId) {
3831
+ if (!conversationId) return emptyDraft();
3832
+ return conversationDrafts.get(conversationId) ?? emptyDraft();
3833
+ }
3834
+ function updateConversationDraft(conversationId, draft) {
3835
+ if (!conversationId) return;
3836
+ if (draft.messageInput.trim() || draft.attachments.length > 0) {
3837
+ conversationDrafts.set(conversationId, {
3838
+ messageInput: draft.messageInput,
3839
+ attachments: draft.attachments
3840
+ });
3841
+ } else {
3842
+ conversationDrafts.delete(conversationId);
3843
+ }
3844
+ notifyDraftChange();
3845
+ }
3846
+ function clearConversationDraft(conversationId) {
3847
+ if (!conversationId) return;
3848
+ if (!conversationDrafts.has(conversationId)) return;
3849
+ conversationDrafts.delete(conversationId);
3850
+ notifyDraftChange();
3851
+ }
3852
+ function getConversationDraftPreview(conversationId) {
3853
+ if (!conversationId) return null;
3854
+ const draft = conversationDrafts.get(conversationId);
3855
+ if (!draft) return null;
3856
+ const text = draft.messageInput.trim();
3857
+ if (text) {
3858
+ return text.length > DRAFT_PREVIEW_MAX_LENGTH ? `${text.slice(0, DRAFT_PREVIEW_MAX_LENGTH)}\u2026` : text;
3859
+ }
3860
+ if (draft.attachments.length > 0) {
3861
+ return draft.attachments.length === 1 ? "Attachment" : `${draft.attachments.length} attachments`;
3862
+ }
3863
+ return null;
3864
+ }
3865
+ function useConversationDraft(conversationId) {
3866
+ const composerRef = useRef(emptyDraft());
3867
+ const syncComposerRef = useCallback((draft) => {
3868
+ composerRef.current = draft;
3869
+ }, []);
3870
+ useEffect(() => {
3871
+ const id = conversationId;
3872
+ return () => {
3873
+ if (!id) return;
3874
+ updateConversationDraft(id, composerRef.current);
3875
+ };
3876
+ }, [conversationId]);
3877
+ return {
3878
+ syncComposerRef
3879
+ };
3880
+ }
3881
+
3882
+ // src/chat/ChannelList.tsx
3758
3883
  var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSearch = "" }) => {
3759
3884
  const onlineUserIds = useStore((s) => s.onlineUsers);
3760
3885
  const allUsers = useStore((s) => s.allUsers);
@@ -3762,12 +3887,14 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3762
3887
  const deletedForMeIds = useStore((s) => s.deletedForMeIds);
3763
3888
  const isFetchingUsers = useStore((s) => s.isFetchingAllUsers);
3764
3889
  const conversations = useStore((s) => s.conversations);
3890
+ const messagesByConversation = useStore((s) => s.messagesByConversation);
3765
3891
  const isFetchingConversations = useStore((s) => s.isFetchingConversations);
3766
3892
  const conversationsHasMore = useStore((s) => s.conversationsHasMore);
3767
3893
  const conversationsPage = useStore((s) => s.conversationsPage);
3768
3894
  const accessToken = useStore((s) => s.accessToken);
3769
3895
  const loggedUserDetails = useStore((s) => s.loggedUserDetails);
3770
3896
  const lastFetchKeyRef = useRef(null);
3897
+ useDraftRevision();
3771
3898
  useEffect(() => {
3772
3899
  if (accessToken) {
3773
3900
  useStore.getState().fetchAllUsers();
@@ -3796,17 +3923,20 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3796
3923
  const isOnline = c.conversationType === "group" || c.isGroup ? false : onlineUserIds.includes(otherParticipantId);
3797
3924
  const unreadCount = typeof c.unreadCount === "number" ? c.unreadCount : 0;
3798
3925
  const isTyping = typingUsersByConv[String(c._id)]?.length > 0;
3799
- let lastMessageContent = c.lastMessage?.content || "No messages yet";
3800
- if (c.lastMessage) {
3801
- if (c.lastMessage.isDeletedForEveryone) {
3926
+ const resolvedLastMessage = resolveConversationLastMessage(c, messagesByConversation);
3927
+ let lastMessageContent = resolvedLastMessage?.content || "No messages yet";
3928
+ if (resolvedLastMessage) {
3929
+ if (resolvedLastMessage.isDeletedForEveryone) {
3802
3930
  lastMessageContent = "This message has been deleted";
3803
- } else if (deletedForMeIds.includes(String(c.lastMessage._id || c.lastMessage.id))) {
3931
+ } else if (deletedForMeIds.includes(String(resolvedLastMessage._id || resolvedLastMessage.id))) {
3804
3932
  lastMessageContent = "";
3805
3933
  }
3806
3934
  }
3935
+ const conversationId = String(c._id);
3936
+ const draftPreview = getConversationDraftPreview(conversationId);
3807
3937
  return {
3808
- id: c.conversationType === "group" || c.isGroup ? String(c._id) : otherParticipantId,
3809
- conversationId: String(c._id),
3938
+ id: c.conversationType === "group" || c.isGroup ? conversationId : otherParticipantId,
3939
+ conversationId,
3810
3940
  name: name.charAt(0).toUpperCase() + name.slice(1),
3811
3941
  image,
3812
3942
  isOnline,
@@ -3815,16 +3945,17 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3815
3945
  isBlocked: c.isBlocked || false,
3816
3946
  unreadCount,
3817
3947
  isTyping,
3948
+ draftPreview,
3818
3949
  pinnedCount: c.pinnedCount,
3819
3950
  starredCount: c.starredCount,
3820
3951
  lastMessage: {
3821
3952
  content: lastMessageContent,
3822
- timestamp: c.lastMessage?.createdAt ? new Date(c.lastMessage.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "",
3953
+ timestamp: resolvedLastMessage?.createdAt ? new Date(resolvedLastMessage.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "",
3823
3954
  sender: "User"
3824
3955
  }
3825
3956
  };
3826
3957
  });
3827
- }, [conversations, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
3958
+ }, [conversations, messagesByConversation, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
3828
3959
  const handleLoadMore = () => {
3829
3960
  if (!isFetchingConversations && conversationsHasMore) {
3830
3961
  useStore.getState().fetchConversations(10, conversationsPage + 1, debouncedSearch);
@@ -3855,8 +3986,10 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3855
3986
  isActive ? "text-(--chat-theme)" : "text-[#0f172a]"
3856
3987
  ) }, channel.name), /* @__PURE__ */ React7__default.createElement("div", { className: "flex shrink-0 flex-col items-end gap-1" }, /* @__PURE__ */ React7__default.createElement("span", { className: "text-[11px] font-medium text-muted-foreground" }, channel.lastMessage?.timestamp), channel.isLeft && /* @__PURE__ */ React7__default.createElement("span", { className: "rounded-full bg-red-50 px-1.5 py-0.5 text-[9px] font-semibold text-red-600" }, "Left"))), /* @__PURE__ */ React7__default.createElement("div", { className: "flex w-full min-w-0 items-center justify-between gap-2 pr-1" }, /* @__PURE__ */ React7__default.createElement("p", { className: cn(
3857
3988
  "min-w-0 line-clamp-1 text-[13px]",
3858
- channel.isTyping ? "font-medium text-(--chat-theme)" : "text-muted-foreground"
3859
- ) }, channel.isTyping ? "typing..." : channel.lastMessage?.content), channel.unreadCount !== void 0 && channel.unreadCount > 0 && /* @__PURE__ */ React7__default.createElement(Badge, { className: cn(
3989
+ channel.isTyping || channel.draftPreview ? "font-medium" : "text-muted-foreground",
3990
+ channel.isTyping && "text-(--chat-theme)",
3991
+ !channel.isTyping && channel.draftPreview && "text-muted-foreground"
3992
+ ) }, channel.isTyping ? "typing..." : channel.draftPreview ? /* @__PURE__ */ React7__default.createElement(React7__default.Fragment, null, /* @__PURE__ */ React7__default.createElement("span", { className: "font-semibold text-rose-500" }, "Draft: "), /* @__PURE__ */ React7__default.createElement("span", null, channel.draftPreview)) : channel.lastMessage?.content), channel.unreadCount !== void 0 && channel.unreadCount > 0 && /* @__PURE__ */ React7__default.createElement(Badge, { className: cn(
3860
3993
  "flex h-5 min-w-5 items-center justify-center rounded-full border-none px-1.5 text-[10px] font-semibold",
3861
3994
  isActive ? "bg-(--chat-theme) text-white" : "bg-(--chat-theme) text-white"
3862
3995
  ) }, channel.unreadCount)))
@@ -5771,10 +5904,9 @@ var MessageReactions = ({
5771
5904
  return /* @__PURE__ */ React7__default.createElement(Tooltip, { key: emoji }, /* @__PURE__ */ React7__default.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React7__default.createElement(
5772
5905
  "button",
5773
5906
  {
5774
- key: emoji,
5775
5907
  onClick: () => onReactionClick(emoji),
5776
5908
  className: cn(
5777
- "flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5",
5909
+ "flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5 outline-none",
5778
5910
  isReactedByMe ? "bg-(--chat-theme-10) border-(--chat-theme-20) text-(--chat-theme)" : "bg-white border-border/40 hover:bg-muted/50 text-[#111827]"
5779
5911
  )
5780
5912
  },
@@ -5837,13 +5969,9 @@ var MessageToolbar = ({
5837
5969
  canEdit = true
5838
5970
  }) => {
5839
5971
  return /* @__PURE__ */ React7__default.createElement("div", { className: cn(
5840
- "absolute -top-10 z-[100] flex items-center gap-1 rounded-full border bg-background/95 p-1 shadow-md backdrop-blur-sm",
5972
+ "absolute -top-10 z-20 flex items-center gap-1 rounded-full border bg-background/95 p-1 shadow-md backdrop-blur-sm",
5841
5973
  isSender ? "right-0" : "left-0"
5842
- ) }, /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: () => {
5843
- onReply?.();
5844
- } }, /* @__PURE__ */ React7__default.createElement(CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: () => {
5845
- onForward?.();
5846
- } }, /* @__PURE__ */ React7__default.createElement(Forward, { className: "size-4" })), /* @__PURE__ */ React7__default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__default.createElement(Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__default.createElement(
5974
+ ) }, /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onReply }, /* @__PURE__ */ React7__default.createElement(CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onForward }, /* @__PURE__ */ React7__default.createElement(Forward, { className: "size-4" })), /* @__PURE__ */ React7__default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__default.createElement(Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__default.createElement(
5847
5975
  PopoverContent,
5848
5976
  {
5849
5977
  className: "w-auto p-0 border-none shadow-2xl rounded-2xl",
@@ -6387,6 +6515,9 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6387
6515
  setState((prev) => ({ ...prev, ...updates }));
6388
6516
  };
6389
6517
  const typingTimeoutRef = useRef(null);
6518
+ const conversationIdRef = useRef(conversationId);
6519
+ conversationIdRef.current = conversationId;
6520
+ const { syncComposerRef } = useConversationDraft(conversationId);
6390
6521
  const emitTypingStart = useStore((s) => s.emitTypingStart);
6391
6522
  const emitTypingStop = useStore((s) => s.emitTypingStop);
6392
6523
  const emitGroupTypingStart = useStore((s) => s.emitGroupTypingStart);
@@ -6398,6 +6529,30 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6398
6529
  React7__default.useEffect(() => {
6399
6530
  attachmentsCountRef.current = state.attachments.length;
6400
6531
  }, [state.attachments.length]);
6532
+ React7__default.useEffect(() => {
6533
+ const draft = {
6534
+ messageInput: state.messageInput,
6535
+ attachments: state.attachments
6536
+ };
6537
+ syncComposerRef(draft);
6538
+ updateConversationDraft(conversationIdRef.current, draft);
6539
+ }, [state.messageInput, state.attachments, syncComposerRef]);
6540
+ React7__default.useEffect(() => {
6541
+ if (typingTimeoutRef.current) {
6542
+ clearTimeout(typingTimeoutRef.current);
6543
+ typingTimeoutRef.current = null;
6544
+ }
6545
+ const draft = getConversationDraft(conversationId);
6546
+ attachmentsCountRef.current = draft.attachments.length;
6547
+ setState((prev) => ({
6548
+ ...prev,
6549
+ messageInput: draft.messageInput,
6550
+ attachments: draft.attachments,
6551
+ attachmentError: "",
6552
+ isTyping: false
6553
+ }));
6554
+ if (fileInputRef.current) fileInputRef.current.value = "";
6555
+ }, [conversationId]);
6401
6556
  const handleInputChange = (e) => {
6402
6557
  updateState({ messageInput: e.target.value });
6403
6558
  if (!conversationId || !loggedUserDetails || !activeChannel) return;
@@ -6482,6 +6637,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6482
6637
  });
6483
6638
  };
6484
6639
  const handleSendMessage = () => {
6640
+ const trimmed = state.messageInput.trim();
6641
+ if (!trimmed && state.attachments.length === 0) return;
6485
6642
  if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
6486
6643
  if (state.isTyping && conversationId && loggedUserDetails && activeChannel) {
6487
6644
  updateState({ isTyping: false });
@@ -6497,7 +6654,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6497
6654
  emitTypingStop(typingPayload);
6498
6655
  }
6499
6656
  }
6500
- onSendMessage?.(state.messageInput.trim(), state.attachments, replyTo);
6657
+ onSendMessage?.(trimmed, state.attachments, replyTo);
6658
+ clearConversationDraft(conversationId);
6501
6659
  updateState({
6502
6660
  messageInput: "",
6503
6661
  attachments: [],
@@ -7027,8 +7185,115 @@ var ChatMain_default = ChatMain;
7027
7185
  init_useStore();
7028
7186
  init_socket_service();
7029
7187
  init_api_service();
7188
+
7189
+ // src/config/socket.config.ts
7190
+ var getSocketConfig = (accessToken, tenantId, apiBaseUrl) => {
7191
+ const baseURL = (apiBaseUrl || "").replace("/api/v1", "");
7192
+ return {
7193
+ url: baseURL,
7194
+ options: {
7195
+ path: "/connection",
7196
+ auth: {
7197
+ accessToken
7198
+ },
7199
+ query: {
7200
+ platform: "web",
7201
+ tenantId: tenantId || "",
7202
+ isTenant: "true"
7203
+ },
7204
+ extraHeaders: {
7205
+ Authorization: `Bearer ${accessToken}`,
7206
+ platform: "web",
7207
+ tenantId: tenantId || "",
7208
+ isTenant: "true"
7209
+ },
7210
+ transports: ["websocket", "polling"],
7211
+ reconnection: true,
7212
+ reconnectionAttempts: Infinity,
7213
+ reconnectionDelay: 2e3,
7214
+ reconnectionDelayMax: 5e3,
7215
+ timeout: 2e4
7216
+ }
7217
+ };
7218
+ };
7219
+ var showNotification = (message, type = "info", icon, duration) => {
7220
+ const options = {
7221
+ icon,
7222
+ duration
7223
+ };
7224
+ switch (type) {
7225
+ case "success":
7226
+ toast.success(message, options);
7227
+ break;
7228
+ case "error":
7229
+ toast.error(message, options);
7230
+ break;
7231
+ case "info":
7232
+ toast(message, options);
7233
+ break;
7234
+ case "warning":
7235
+ toast(message, { ...options, icon: "\u26A0\uFE0F" });
7236
+ break;
7237
+ default:
7238
+ toast(message, options);
7239
+ }
7240
+ };
7241
+ var downloadFile = async (src, name) => {
7242
+ try {
7243
+ const link = document.createElement("a");
7244
+ link.href = src;
7245
+ link.setAttribute("download", name);
7246
+ link.setAttribute("rel", "noopener noreferrer");
7247
+ link.click();
7248
+ showNotification("File Downloaded Successfully", "success");
7249
+ } catch (err) {
7250
+ console.error("File download failed:", err);
7251
+ showNotification("Failed to download file", "error");
7252
+ }
7253
+ };
7254
+ var getResponsiveWidth = (width) => {
7255
+ const screenWidth = window.innerWidth;
7256
+ if (width) {
7257
+ return width;
7258
+ } else if (screenWidth < 640) {
7259
+ return 350;
7260
+ } else if (screenWidth >= 640 && screenWidth < 1024) {
7261
+ return 600;
7262
+ } else if (screenWidth >= 1024 && screenWidth < 1280) {
7263
+ return 800;
7264
+ } else {
7265
+ return 1e3;
7266
+ }
7267
+ };
7268
+ var isEmpty = (value) => {
7269
+ if (value === null || value === void 0) return true;
7270
+ if (typeof value === "string") return value.trim().length === 0;
7271
+ if (Array.isArray(value)) {
7272
+ return value.length === 0 || value.every((item) => isEmpty(item));
7273
+ }
7274
+ if (typeof value === "object") {
7275
+ return Object.keys(value).length === 0;
7276
+ }
7277
+ return false;
7278
+ };
7279
+ function uniqueList(list, key) {
7280
+ const seen = /* @__PURE__ */ new Set();
7281
+ return list.filter((item) => {
7282
+ const value = item[key];
7283
+ if (seen.has(value)) return false;
7284
+ seen.add(value);
7285
+ return true;
7286
+ });
7287
+ }
7288
+ var capitalizeFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
7289
+ var getFirstAndLastNameOneCharacter = (name) => {
7290
+ const names = name.split(" ");
7291
+ return names.map((name2) => name2.charAt(0).toUpperCase()).join("");
7292
+ };
7293
+
7294
+ // src/index.ts
7030
7295
  var index_default = ChatMain_default;
7031
7296
 
7032
- export { Channel, ChannelHeader_default as ChannelHeader, ChannelList_default as ChannelList, Chat, ChatLayout_default as ChatLayout, ChatMain_default as ChatMain, ForwardModal_default as ForwardModal, LoggedUserDetails_default as LoggedUserDetails, ChannelMessageBox_default as MessageInput, ActiveChannelMessages_default as MessageList, index_default as default, defaultChatLayoutConfig, mergeChatLayoutConfig, setChatAccessToken, setChatBaseURL, setChatClientId, socket_service_default as socketService, useChatContext, useChatController, useStore as useChatStore };
7297
+ export { Channel, ChannelHeader_default as ChannelHeader, ChannelList_default as ChannelList, Chat, ChatLayout_default as ChatLayout, ChatMain_default as ChatMain, ForwardModal_default as ForwardModal, LoggedUserDetails_default as LoggedUserDetails, ChannelMessageBox_default as MessageInput, ActiveChannelMessages_default as MessageList, capitalizeFirst, index_default as default, defaultChatLayoutConfig, downloadFile, getFirstAndLastNameOneCharacter, getResponsiveWidth, getSocketConfig, isEmpty, mergeChatLayoutConfig, setChatAccessToken, setChatBaseURL, setChatClientId, showNotification, socket_service_default as socketService, uniqueList, useChatContext, useChatController, useStore as useChatStore };
7033
7298
  //# sourceMappingURL=index.mjs.map
7034
7299
  //# sourceMappingURL=index.mjs.map