@realtimexsco/live-chat 1.0.0 → 1.2.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.cjs CHANGED
@@ -13,6 +13,7 @@ var tailwindMerge = require('tailwind-merge');
13
13
  var lucideReact = require('lucide-react');
14
14
  var classVarianceAuthority = require('class-variance-authority');
15
15
  var EmojiPicker = require('emoji-picker-react');
16
+ var reactHotToast = require('react-hot-toast');
16
17
 
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
19
 
@@ -169,11 +170,28 @@ var init_socket_events_constants = __esm({
169
170
  };
170
171
  }
171
172
  });
172
- var SocketService, socketService; exports.socketService = void 0;
173
+
174
+ // src/store/useSocketStore.ts
175
+ var useSocketStore_exports = {};
176
+ __export(useSocketStore_exports, {
177
+ default: () => useSocketStore_default,
178
+ useSocketStore: () => useSocketStore
179
+ });
180
+ var useSocketStore, useSocketStore_default;
181
+ var init_useSocketStore = __esm({
182
+ "src/store/useSocketStore.ts"() {
183
+ init_useStore();
184
+ useSocketStore = exports.useChatStore;
185
+ useSocketStore_default = useSocketStore;
186
+ }
187
+ });
188
+ var getStore, SocketService, socketService; exports.socketService = void 0;
173
189
  var init_socket_service = __esm({
174
190
  "src/services/socket.service.ts"() {
175
191
  init_socket_events_constants();
176
- init_useStore();
192
+ getStore = () => {
193
+ return (init_useSocketStore(), __toCommonJS(useSocketStore_exports)).useSocketStore;
194
+ };
177
195
  SocketService = class {
178
196
  constructor() {
179
197
  this.socket = null;
@@ -192,7 +210,7 @@ var init_socket_service = __esm({
192
210
  }
193
211
  this.currentConfig = config;
194
212
  this.isConnecting = true;
195
- const { setConnecting, setConnected, setDisconnected, setError } = useStore_default.getState();
213
+ const { setConnecting, setConnected, setDisconnected, setError } = getStore().getState();
196
214
  setConnecting();
197
215
  this.socket = socket_ioClient.io(config.url, config.options);
198
216
  this.socket.on("connect", () => {
@@ -210,72 +228,72 @@ var init_socket_service = __esm({
210
228
  });
211
229
  this.socket.on(SOCKET_EVENTS.ERROR, (payload) => {
212
230
  const message = typeof payload === "string" ? payload : payload?.message ?? "app_error";
213
- useStore_default.getState().handleAppError(message);
231
+ getStore().getState().handleAppError(message);
214
232
  });
215
233
  this.socket.on(SOCKET_EVENTS.DM_RECEIVE, (payload) => {
216
- useStore_default.getState().receivedDM(payload);
234
+ getStore().getState().receivedDM(payload);
217
235
  });
218
236
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_RECEIVE, (payload) => {
219
- useStore_default.getState().receivedGroupMessage(payload);
237
+ getStore().getState().receivedGroupMessage(payload);
220
238
  });
221
239
  this.socket.on(SOCKET_EVENTS.CONVERSATION_UPDATE, (payload) => {
222
240
  if (!payload) return;
223
- useStore_default.getState().updateConversation(payload);
241
+ getStore().getState().updateConversation(payload);
224
242
  });
225
243
  this.socket.on(SOCKET_EVENTS.DM_EDIT_RECEIVE, (payload) => {
226
- useStore_default.getState().receivedEditDM(payload);
244
+ getStore().getState().receivedEditDM(payload);
227
245
  });
228
246
  this.socket.on(SOCKET_EVENTS.DM_DELETE_ME, (payload) => {
229
- useStore_default.getState().receivedDeleteForMe(payload);
247
+ getStore().getState().receivedDeleteForMe(payload);
230
248
  });
231
249
  this.socket.on(SOCKET_EVENTS.DM_DELETE_EVERYONE, (payload) => {
232
- useStore_default.getState().receivedDeleteForEveryone(payload);
250
+ getStore().getState().receivedDeleteForEveryone(payload);
233
251
  });
234
252
  this.socket.on(SOCKET_EVENTS.DM_CLEAR_CHAT_SUCCESS, (payload) => {
235
- useStore_default.getState().receivedDmClearChat(payload);
253
+ getStore().getState().receivedDmClearChat(payload);
236
254
  });
237
255
  this.socket.on(SOCKET_EVENTS.GROUP_CLEAR_CHAT_SUCCESS, (payload) => {
238
- useStore_default.getState().receivedGroupClearChat(payload);
256
+ getStore().getState().receivedGroupClearChat(payload);
239
257
  });
240
258
  this.socket.on(SOCKET_EVENTS.DM_DELETE_CONVERSATION_SUCCESS, (payload) => {
241
- useStore_default.getState().receivedDmDeleteConversation(payload);
259
+ getStore().getState().receivedDmDeleteConversation(payload);
242
260
  });
243
261
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION_SUCCESS, (payload) => {
244
- useStore_default.getState().receivedGroupDeleteConversation(payload);
262
+ getStore().getState().receivedGroupDeleteConversation(payload);
245
263
  });
246
264
  this.socket.on(SOCKET_EVENTS.DM_REACTION_ADD_RECEIVE, (payload) => {
247
- useStore_default.getState().receivedReactionAdd(payload);
265
+ getStore().getState().receivedReactionAdd(payload);
248
266
  });
249
267
  this.socket.on(SOCKET_EVENTS.DM_REACTION_REMOVE_RECEIVE, (payload) => {
250
- useStore_default.getState().receivedReactionRemove(payload);
268
+ getStore().getState().receivedReactionRemove(payload);
251
269
  });
252
270
  this.socket.on(SOCKET_EVENTS.DM_PIN_UPDATE, (payload) => {
253
- useStore_default.getState().receivedPinUpdate(payload);
271
+ getStore().getState().receivedPinUpdate(payload);
254
272
  });
255
273
  this.socket.on(SOCKET_EVENTS.DM_STAR_UPDATE, (payload) => {
256
- useStore_default.getState().receivedStarUpdate(payload);
274
+ getStore().getState().receivedStarUpdate(payload);
257
275
  });
258
276
  this.socket.on(SOCKET_EVENTS.DM_STATUS_UPDATE, (payload) => {
259
- useStore_default.getState().receivedStatusUpdate(payload);
277
+ getStore().getState().receivedStatusUpdate(payload);
260
278
  });
261
279
  this.socket.on(SOCKET_EVENTS.DM_MARK_READ, (payload) => {
262
- useStore_default.getState().receivedDMMarkRead(payload);
280
+ getStore().getState().receivedDMMarkRead(payload);
263
281
  });
264
282
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_READ, (payload) => {
265
- useStore_default.getState().receivedGroupMarkRead(payload);
283
+ getStore().getState().receivedGroupMarkRead(payload);
266
284
  });
267
285
  this.socket.on(SOCKET_EVENTS.DM_FORWARD_RECEIVE, (payload) => {
268
- useStore_default.getState().receivedForward(payload);
286
+ getStore().getState().receivedForward(payload);
269
287
  });
270
288
  this.socket.on(SOCKET_EVENTS.DM_TYPING_START, (payload) => {
271
- useStore_default.getState().setTypingStart(payload);
289
+ getStore().getState().setTypingStart(payload);
272
290
  });
273
291
  this.socket.on(SOCKET_EVENTS.DM_TYPING_STOP, (payload) => {
274
- useStore_default.getState().setTypingStop(payload);
292
+ getStore().getState().setTypingStop(payload);
275
293
  });
276
294
  this.socket.on(SOCKET_EVENTS.ONLINE_USERS_LIST, (payload) => {
277
295
  const users = payload?.onlineUsers || [];
278
- useStore_default.getState().setOnlineUsers(users);
296
+ getStore().getState().setOnlineUsers(users);
279
297
  });
280
298
  this.socket.on(SOCKET_EVENTS.USER_ONLINE, () => {
281
299
  this.emitGetOnlineUsers();
@@ -284,49 +302,49 @@ var init_socket_service = __esm({
284
302
  this.emitGetOnlineUsers();
285
303
  });
286
304
  this.socket.on(SOCKET_EVENTS.CONVERSATION_STARRED_PINNED_COUNT_UPDATE, (payload) => {
287
- useStore_default.getState().receivedCountUpdate(payload);
305
+ getStore().getState().receivedCountUpdate(payload);
288
306
  });
289
307
  this.socket.on(SOCKET_EVENTS.GROUP_EDIT_RECEIVE, (payload) => {
290
- useStore_default.getState().receivedEditDM(payload);
308
+ getStore().getState().receivedEditDM(payload);
291
309
  });
292
310
  this.socket.on(SOCKET_EVENTS.GROUP_STATUS_UPDATE, (payload) => {
293
- useStore_default.getState().receivedGroupStatusUpdate(payload);
311
+ getStore().getState().receivedGroupStatusUpdate(payload);
294
312
  });
295
313
  this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_DELIVERED, (payload) => {
296
- useStore_default.getState().receivedGroupStatusUpdate({
314
+ getStore().getState().receivedGroupStatusUpdate({
297
315
  ...payload,
298
316
  status: payload?.status || "delivered"
299
317
  });
300
318
  });
301
319
  this.socket.on(SOCKET_EVENTS.GROUP_REACTION_ADD_RECEIVE, (payload) => {
302
- useStore_default.getState().receivedGroupReactionAdd(payload);
320
+ getStore().getState().receivedGroupReactionAdd(payload);
303
321
  });
304
322
  this.socket.on(SOCKET_EVENTS.GROUP_TYPING_START, (payload) => {
305
- useStore_default.getState().setTypingStart(payload);
323
+ getStore().getState().setTypingStart(payload);
306
324
  });
307
325
  this.socket.on(SOCKET_EVENTS.GROUP_TYPING_STOP, (payload) => {
308
- useStore_default.getState().setTypingStop(payload);
326
+ getStore().getState().setTypingStop(payload);
309
327
  });
310
328
  this.socket.on(SOCKET_EVENTS.GROUP_REACTION_REMOVE_RECEIVE, (payload) => {
311
- useStore_default.getState().receivedGroupReactionRemove(payload);
329
+ getStore().getState().receivedGroupReactionRemove(payload);
312
330
  });
313
331
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_ME, (payload) => {
314
- useStore_default.getState().receivedDeleteForMe(payload);
332
+ getStore().getState().receivedDeleteForMe(payload);
315
333
  });
316
334
  this.socket.on(SOCKET_EVENTS.GROUP_DELETE_EVERYONE, (payload) => {
317
- useStore_default.getState().receivedDeleteForEveryone(payload);
335
+ getStore().getState().receivedDeleteForEveryone(payload);
318
336
  });
319
337
  this.socket.on(SOCKET_EVENTS.GROUP_PIN_UPDATE, (payload) => {
320
- useStore_default.getState().receivedPinUpdate(payload);
338
+ getStore().getState().receivedPinUpdate(payload);
321
339
  });
322
340
  this.socket.on(SOCKET_EVENTS.GROUP_STAR_UPDATE, (payload) => {
323
- useStore_default.getState().receivedStarUpdate(payload);
341
+ getStore().getState().receivedStarUpdate(payload);
324
342
  });
325
343
  this.socket.on(SOCKET_EVENTS.GROUP_FORWARD_RECEIVE, (payload) => {
326
- useStore_default.getState().receivedForward(payload);
344
+ getStore().getState().receivedForward(payload);
327
345
  });
328
346
  this.socket.on(SOCKET_EVENTS.GROUP_LEAVE_RECEIVE, (payload) => {
329
- useStore_default.getState().receivedGroupLeave(payload);
347
+ getStore().getState().receivedGroupLeave(payload);
330
348
  });
331
349
  }
332
350
  disconnect() {
@@ -456,6 +474,176 @@ var init_socket_service = __esm({
456
474
  }
457
475
  });
458
476
 
477
+ // src/store/helpers/store.helpers.ts
478
+ var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, mergeLastMessage, getLatestMessageFromList, resolveConversationLastMessage, syncConversationLastMessage, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
479
+ var init_store_helpers = __esm({
480
+ "src/store/helpers/store.helpers.ts"() {
481
+ findConversationIdByMessageId = (messagesByConversation, messageId) => {
482
+ return Object.keys(messagesByConversation).find(
483
+ (cid) => messagesByConversation[cid].some(
484
+ (m) => String(m._id || m.id) === String(messageId)
485
+ )
486
+ );
487
+ };
488
+ normalizeId = (id) => String(id?._id || id?.id || id || "");
489
+ normalizeUnreadCount = (unreadCount) => {
490
+ if (typeof unreadCount === "number") return unreadCount;
491
+ return 0;
492
+ };
493
+ incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
494
+ emptyMessagesMetadata = () => ({
495
+ nextCursor: null,
496
+ hasMore: false,
497
+ isFirstPage: true
498
+ });
499
+ extractMessageData = (payload) => {
500
+ const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
501
+ const content = payload.content || payload.message?.content;
502
+ const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
503
+ const status = payload.status || payload.message?.status;
504
+ const isPinned = payload.isPinned ?? payload.message?.isPinned;
505
+ const isStarred = payload.isStarred ?? payload.message?.isStarred;
506
+ return { messageId, content, conversationId, status, isPinned, isStarred };
507
+ };
508
+ mergeLastMessage = (existing, incoming) => {
509
+ if (!incoming) return existing ?? null;
510
+ if (!existing) return incoming;
511
+ const content = incoming.content?.trim() ? incoming.content : existing.content;
512
+ const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
513
+ return {
514
+ ...existing,
515
+ ...incoming,
516
+ ...content !== void 0 ? { content } : {},
517
+ ...createdAt !== void 0 ? { createdAt } : {}
518
+ };
519
+ };
520
+ getLatestMessageFromList = (messages) => {
521
+ if (!messages?.length) return null;
522
+ return messages.reduce((latest, message) => {
523
+ const latestTime = new Date(latest.createdAt || latest.timestamp || 0).getTime();
524
+ const messageTime = new Date(message.createdAt || message.timestamp || 0).getTime();
525
+ return messageTime >= latestTime ? message : latest;
526
+ });
527
+ };
528
+ resolveConversationLastMessage = (conversation, messagesByConversation) => {
529
+ const conversationId = String(conversation._id || conversation.id || "");
530
+ const storedLast = conversation.lastMessage;
531
+ if (storedLast?.content?.trim()) return storedLast;
532
+ const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
533
+ return mergeLastMessage(storedLast, latestFromMessages);
534
+ };
535
+ syncConversationLastMessage = (conversations, conversationId, messages) => {
536
+ const latest = getLatestMessageFromList(messages);
537
+ if (!latest) return conversations;
538
+ return conversations.map((conversation) => {
539
+ if (String(conversation._id) !== String(conversationId)) return conversation;
540
+ return {
541
+ ...conversation,
542
+ lastMessage: mergeLastMessage(conversation.lastMessage, latest)
543
+ };
544
+ });
545
+ };
546
+ isGroupConversation = (conversation) => {
547
+ if (!conversation) return false;
548
+ return conversation.conversationType === "group" || !!conversation.isGroup;
549
+ };
550
+ findConversationById = (conversations, conversationId) => {
551
+ if (!conversationId) return void 0;
552
+ return conversations.find((c) => String(c._id) === String(conversationId));
553
+ };
554
+ resolveIsGroupFromPayload = (payload, conversations) => {
555
+ const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
556
+ const fromStore = findConversationById(conversations, conversationId);
557
+ if (fromStore) return isGroupConversation(fromStore);
558
+ return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
559
+ };
560
+ getOtherParticipantId = (conversation, loggedUserId) => {
561
+ if (!conversation?.participants?.length) return "";
562
+ const other = conversation.participants.find(
563
+ (p) => String(p._id || p) !== String(loggedUserId)
564
+ ) || conversation.participants[0];
565
+ return String(other?._id || other || "");
566
+ };
567
+ findConversationForChannel = (channel, loggedUserId, conversations) => {
568
+ if (!channel || !loggedUserId) return void 0;
569
+ if (channel.conversationId) {
570
+ const byId = findConversationById(conversations, channel.conversationId);
571
+ if (byId) return byId;
572
+ }
573
+ if (channel.isGroup) {
574
+ return conversations.find(
575
+ (c) => isGroupConversation(c) && String(c._id) === String(channel.id)
576
+ );
577
+ }
578
+ return conversations.find((c) => {
579
+ if (isGroupConversation(c)) return false;
580
+ const ids = (c.participants || []).map((p) => String(p._id || p));
581
+ return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
582
+ });
583
+ };
584
+ resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
585
+ const found = findConversationForChannel(channel, loggedUserId, conversations);
586
+ if (found?._id) return String(found._id);
587
+ if (channel?.conversationId) return String(channel.conversationId);
588
+ return null;
589
+ };
590
+ getMessageContent = (message) => String(message?.content || "").trim();
591
+ getMessageTimestamp = (message) => {
592
+ const value = message?.createdAt || message?.timestamp;
593
+ const time = value ? new Date(value).getTime() : NaN;
594
+ return Number.isFinite(time) ? time : 0;
595
+ };
596
+ isOwnMessage = (message, loggedUserId) => {
597
+ if (message?.isOwnMessage) return true;
598
+ if (!loggedUserId) return false;
599
+ const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
600
+ return String(senderId) === String(loggedUserId);
601
+ };
602
+ STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
603
+ pickBetterMessageStatus = (current, incoming) => {
604
+ if (!incoming) return current;
605
+ if (!current) return incoming;
606
+ return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
607
+ };
608
+ mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
609
+ const merged = /* @__PURE__ */ new Map();
610
+ apiMessages.forEach((message) => {
611
+ const id = String(message?._id || message?.id || "");
612
+ if (id) merged.set(id, message);
613
+ });
614
+ localMessages.forEach((localMessage) => {
615
+ if (!localMessage) return;
616
+ const localId = String(localMessage._id || localMessage.id || "");
617
+ if (localId && merged.has(localId)) {
618
+ const existing = merged.get(localId);
619
+ merged.set(localId, {
620
+ ...existing,
621
+ ...localMessage,
622
+ status: pickBetterMessageStatus(existing?.status, localMessage?.status)
623
+ });
624
+ return;
625
+ }
626
+ const duplicateInApi = apiMessages.some((apiMessage) => {
627
+ const apiId = String(apiMessage?._id || apiMessage?.id || "");
628
+ if (localId && apiId && localId === apiId) return true;
629
+ if (!isOwnMessage(localMessage, loggedUserId)) return false;
630
+ if (!isOwnMessage(apiMessage, loggedUserId)) return false;
631
+ if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
632
+ return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
633
+ });
634
+ if (duplicateInApi) return;
635
+ const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
636
+ if (!merged.has(key)) merged.set(key, localMessage);
637
+ });
638
+ return Array.from(merged.values()).filter((message, index, list) => {
639
+ const id = String(message?._id || message?.id || "");
640
+ if (!id) return true;
641
+ return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
642
+ });
643
+ };
644
+ }
645
+ });
646
+
459
647
  // src/constants/api.constants.ts
460
648
  var API_ENDPOINTS;
461
649
  var init_api_constants = __esm({
@@ -483,21 +671,6 @@ var init_api_constants = __esm({
483
671
  };
484
672
  }
485
673
  });
486
-
487
- // src/store/useSocketStore.ts
488
- var useSocketStore_exports = {};
489
- __export(useSocketStore_exports, {
490
- default: () => useSocketStore_default,
491
- useSocketStore: () => useSocketStore
492
- });
493
- var useSocketStore, useSocketStore_default;
494
- var init_useSocketStore = __esm({
495
- "src/store/useSocketStore.ts"() {
496
- init_useStore();
497
- useSocketStore = exports.useChatStore;
498
- useSocketStore_default = useSocketStore;
499
- }
500
- });
501
674
  var chatClientId, chatAccessToken, rateLimitResetAt; exports.setChatClientId = void 0; exports.setChatAccessToken = void 0; var isRateLimited, apiClient; exports.setChatBaseURL = void 0; var clientUserLogin, fetchUsers, fetchConversations, fetchMessages, fetchPinnedMessages, fetchStarredMessages, createGroup, fetchConversationInfo, updateConversation, updateGroupInformation, addRemoveGroupMembers, addRemoveGroupAdmins, searchMessages, searchMessagesContext, blockUnblockUser;
502
675
  var init_api_service = __esm({
503
676
  "src/services/api.service.ts"() {
@@ -718,138 +891,6 @@ var init_api_service = __esm({
718
891
  }
719
892
  });
720
893
 
721
- // src/store/helpers/store.helpers.ts
722
- var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
723
- var init_store_helpers = __esm({
724
- "src/store/helpers/store.helpers.ts"() {
725
- findConversationIdByMessageId = (messagesByConversation, messageId) => {
726
- return Object.keys(messagesByConversation).find(
727
- (cid) => messagesByConversation[cid].some(
728
- (m) => String(m._id || m.id) === String(messageId)
729
- )
730
- );
731
- };
732
- normalizeId = (id) => String(id?._id || id?.id || id || "");
733
- normalizeUnreadCount = (unreadCount) => {
734
- if (typeof unreadCount === "number") return unreadCount;
735
- return 0;
736
- };
737
- incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
738
- emptyMessagesMetadata = () => ({
739
- nextCursor: null,
740
- hasMore: false,
741
- isFirstPage: true
742
- });
743
- extractMessageData = (payload) => {
744
- const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
745
- const content = payload.content || payload.message?.content;
746
- const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
747
- const status = payload.status || payload.message?.status;
748
- const isPinned = payload.isPinned ?? payload.message?.isPinned;
749
- const isStarred = payload.isStarred ?? payload.message?.isStarred;
750
- return { messageId, content, conversationId, status, isPinned, isStarred };
751
- };
752
- isGroupConversation = (conversation) => {
753
- if (!conversation) return false;
754
- return conversation.conversationType === "group" || !!conversation.isGroup;
755
- };
756
- findConversationById = (conversations, conversationId) => {
757
- if (!conversationId) return void 0;
758
- return conversations.find((c) => String(c._id) === String(conversationId));
759
- };
760
- resolveIsGroupFromPayload = (payload, conversations) => {
761
- const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
762
- const fromStore = findConversationById(conversations, conversationId);
763
- if (fromStore) return isGroupConversation(fromStore);
764
- return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
765
- };
766
- getOtherParticipantId = (conversation, loggedUserId) => {
767
- if (!conversation?.participants?.length) return "";
768
- const other = conversation.participants.find(
769
- (p) => String(p._id || p) !== String(loggedUserId)
770
- ) || conversation.participants[0];
771
- return String(other?._id || other || "");
772
- };
773
- findConversationForChannel = (channel, loggedUserId, conversations) => {
774
- if (!channel || !loggedUserId) return void 0;
775
- if (channel.conversationId) {
776
- const byId = findConversationById(conversations, channel.conversationId);
777
- if (byId) return byId;
778
- }
779
- if (channel.isGroup) {
780
- return conversations.find(
781
- (c) => isGroupConversation(c) && String(c._id) === String(channel.id)
782
- );
783
- }
784
- return conversations.find((c) => {
785
- if (isGroupConversation(c)) return false;
786
- const ids = (c.participants || []).map((p) => String(p._id || p));
787
- return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
788
- });
789
- };
790
- resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
791
- const found = findConversationForChannel(channel, loggedUserId, conversations);
792
- if (found?._id) return String(found._id);
793
- if (channel?.conversationId) return String(channel.conversationId);
794
- return null;
795
- };
796
- getMessageContent = (message) => String(message?.content || "").trim();
797
- getMessageTimestamp = (message) => {
798
- const value = message?.createdAt || message?.timestamp;
799
- const time = value ? new Date(value).getTime() : NaN;
800
- return Number.isFinite(time) ? time : 0;
801
- };
802
- isOwnMessage = (message, loggedUserId) => {
803
- if (message?.isOwnMessage) return true;
804
- if (!loggedUserId) return false;
805
- const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
806
- return String(senderId) === String(loggedUserId);
807
- };
808
- STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
809
- pickBetterMessageStatus = (current, incoming) => {
810
- if (!incoming) return current;
811
- if (!current) return incoming;
812
- return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
813
- };
814
- mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
815
- const merged = /* @__PURE__ */ new Map();
816
- apiMessages.forEach((message) => {
817
- const id = String(message?._id || message?.id || "");
818
- if (id) merged.set(id, message);
819
- });
820
- localMessages.forEach((localMessage) => {
821
- if (!localMessage) return;
822
- const localId = String(localMessage._id || localMessage.id || "");
823
- if (localId && merged.has(localId)) {
824
- const existing = merged.get(localId);
825
- merged.set(localId, {
826
- ...existing,
827
- ...localMessage,
828
- status: pickBetterMessageStatus(existing?.status, localMessage?.status)
829
- });
830
- return;
831
- }
832
- const duplicateInApi = apiMessages.some((apiMessage) => {
833
- const apiId = String(apiMessage?._id || apiMessage?.id || "");
834
- if (localId && apiId && localId === apiId) return true;
835
- if (!isOwnMessage(localMessage, loggedUserId)) return false;
836
- if (!isOwnMessage(apiMessage, loggedUserId)) return false;
837
- if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
838
- return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
839
- });
840
- if (duplicateInApi) return;
841
- const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
842
- if (!merged.has(key)) merged.set(key, localMessage);
843
- });
844
- return Array.from(merged.values()).filter((message, index, list) => {
845
- const id = String(message?._id || message?.id || "");
846
- if (!id) return true;
847
- return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
848
- });
849
- };
850
- }
851
- });
852
-
853
894
  // src/store/forwardPending.store.ts
854
895
  var pendingForwards, lastPendingMessageId, setPendingForward, peekPendingForward, peekMostRecentPendingForward, getLastPendingForwardMessageId, clearPendingForward;
855
896
  var init_forwardPending_store = __esm({
@@ -1497,8 +1538,8 @@ var init_socket_slice = __esm({
1497
1538
  "src/store/slices/socket.slice.ts"() {
1498
1539
  init_socket_service();
1499
1540
  init_socket_events_constants();
1500
- init_api_service();
1501
1541
  init_store_helpers();
1542
+ init_api_service();
1502
1543
  init_forwardPending_store();
1503
1544
  init_message_handlers();
1504
1545
  init_group_handlers();
@@ -1962,6 +2003,7 @@ var init_socket_slice = __esm({
1962
2003
  }));
1963
2004
  },
1964
2005
  // Fetching
2006
+ // Fetching
1965
2007
  fetchConversations: async (limit = 10, page = 1, search = "", force = false) => {
1966
2008
  const fetchKey = `${limit}:${page}:${search}`;
1967
2009
  const state = get();
@@ -1974,7 +2016,15 @@ var init_socket_slice = __esm({
1974
2016
  const response = await fetchConversations(limit, page, search);
1975
2017
  const data = safelyExtractArray(response);
1976
2018
  set((state2) => ({
1977
- conversations: page === 1 ? data : [...state2.conversations, ...data],
2019
+ conversations: page === 1 ? data.map((incoming) => {
2020
+ const existing = state2.conversations.find(
2021
+ (conversation) => String(conversation._id) === String(incoming._id)
2022
+ );
2023
+ return {
2024
+ ...incoming,
2025
+ lastMessage: mergeLastMessage(existing?.lastMessage, incoming.lastMessage)
2026
+ };
2027
+ }) : [...state2.conversations, ...data],
1978
2028
  conversationsHasMore: data.length === limit,
1979
2029
  conversationsPage: page,
1980
2030
  isFetchingConversations: false
@@ -2026,6 +2076,7 @@ var init_socket_slice = __esm({
2026
2076
  isFirstPage: isInitialLoad
2027
2077
  }
2028
2078
  },
2079
+ conversations: isInitialLoad ? syncConversationLastMessage(state2.conversations, conversationId, unique) : state2.conversations,
2029
2080
  isFetchingMessages: false
2030
2081
  };
2031
2082
  });
@@ -2055,6 +2106,7 @@ var init_socket_slice = __esm({
2055
2106
  const tempId = payload.tempId || `temp-${Date.now()}`;
2056
2107
  set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [tempId]: ["send"] } }));
2057
2108
  exports.socketService.emit("dm_send", { ...payload, tempId });
2109
+ exports.socketService.emit("conversation_update", { conversationId: payload.conversationId });
2058
2110
  },
2059
2111
  emitDmEdit: (payload) => {
2060
2112
  set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [payload.messageId]: ["edit"] } }));
@@ -2193,27 +2245,22 @@ var init_socket_slice = __esm({
2193
2245
  const { conversationId } = payload;
2194
2246
  const readerId = payload.userId || payload.readerId || payload.readBy || payload.sender || payload.senderId;
2195
2247
  if (!conversationId) return;
2196
- const readStatus = payload.status || "read";
2197
2248
  set({ lastDMMarkRead: payload });
2198
2249
  set((state) => {
2199
2250
  const messages = state.messagesByConversation[conversationId] || [];
2200
2251
  const loggedUserDetails = get().loggedUserDetails || state.loggedUserDetails;
2201
2252
  const myId = loggedUserDetails?._id || loggedUserDetails?.id;
2202
2253
  const isReaderMe = String(readerId) === String(myId);
2203
- const nextMessages = messages.map((m) => {
2204
- if (isReaderMe) return m;
2205
- const senderId = String(m.sender?._id || m.sender);
2206
- if (senderId === String(myId)) {
2207
- return { ...m, status: readStatus };
2208
- }
2209
- return m;
2210
- });
2254
+ const nextMessages = messages.map((m) => ({ ...m, status: "read" }));
2211
2255
  const nextConversations = state.conversations.map((c) => {
2212
2256
  if (String(c._id) !== String(conversationId)) return c;
2213
2257
  const nextUnreadCount = isReaderMe ? 0 : normalizeUnreadCount(c.unreadCount);
2214
- let nextLastMessage = c.lastMessage;
2215
- if (nextLastMessage && !isReaderMe && String(nextLastMessage.sender?._id || nextLastMessage.sender) === String(myId)) {
2216
- nextLastMessage = { ...nextLastMessage, status: readStatus };
2258
+ let nextLastMessage = mergeLastMessage(c.lastMessage, { status: "read" });
2259
+ if (!nextLastMessage?.content && messages.length) {
2260
+ nextLastMessage = mergeLastMessage(
2261
+ getLatestMessageFromList(messages),
2262
+ { status: "read" }
2263
+ );
2217
2264
  }
2218
2265
  return { ...c, unreadCount: nextUnreadCount, lastMessage: nextLastMessage };
2219
2266
  });
@@ -2375,7 +2422,7 @@ var init_socket_slice = __esm({
2375
2422
  return {
2376
2423
  ...c,
2377
2424
  ...updated,
2378
- lastMessage: updated.lastMessage || c.lastMessage,
2425
+ lastMessage: mergeLastMessage(c.lastMessage, updated.lastMessage),
2379
2426
  // Group conversation_update no longer carries per-user unread; keep local count
2380
2427
  unreadCount: updated.unreadCount !== void 0 ? normalizeUnreadCount(updated.unreadCount) : normalizeUnreadCount(c.unreadCount)
2381
2428
  };
@@ -3782,6 +3829,84 @@ function ScrollBar({
3782
3829
  );
3783
3830
  }
3784
3831
  init_useStore();
3832
+ init_store_helpers();
3833
+ var conversationDrafts = /* @__PURE__ */ new Map();
3834
+ var DRAFT_PREVIEW_MAX_LENGTH = 50;
3835
+ var emptyDraft = () => ({
3836
+ messageInput: "",
3837
+ attachments: []
3838
+ });
3839
+ var draftRevision = 0;
3840
+ var draftListeners = /* @__PURE__ */ new Set();
3841
+ function notifyDraftChange() {
3842
+ draftRevision += 1;
3843
+ draftListeners.forEach((listener) => listener());
3844
+ }
3845
+ function subscribeToDrafts(listener) {
3846
+ draftListeners.add(listener);
3847
+ return () => {
3848
+ draftListeners.delete(listener);
3849
+ };
3850
+ }
3851
+ function getDraftRevision() {
3852
+ return draftRevision;
3853
+ }
3854
+ function useDraftRevision() {
3855
+ return React7.useSyncExternalStore(subscribeToDrafts, getDraftRevision, getDraftRevision);
3856
+ }
3857
+ function getConversationDraft(conversationId) {
3858
+ if (!conversationId) return emptyDraft();
3859
+ return conversationDrafts.get(conversationId) ?? emptyDraft();
3860
+ }
3861
+ function updateConversationDraft(conversationId, draft) {
3862
+ if (!conversationId) return;
3863
+ if (draft.messageInput.trim() || draft.attachments.length > 0) {
3864
+ conversationDrafts.set(conversationId, {
3865
+ messageInput: draft.messageInput,
3866
+ attachments: draft.attachments
3867
+ });
3868
+ } else {
3869
+ conversationDrafts.delete(conversationId);
3870
+ }
3871
+ notifyDraftChange();
3872
+ }
3873
+ function clearConversationDraft(conversationId) {
3874
+ if (!conversationId) return;
3875
+ if (!conversationDrafts.has(conversationId)) return;
3876
+ conversationDrafts.delete(conversationId);
3877
+ notifyDraftChange();
3878
+ }
3879
+ function getConversationDraftPreview(conversationId) {
3880
+ if (!conversationId) return null;
3881
+ const draft = conversationDrafts.get(conversationId);
3882
+ if (!draft) return null;
3883
+ const text = draft.messageInput.trim();
3884
+ if (text) {
3885
+ return text.length > DRAFT_PREVIEW_MAX_LENGTH ? `${text.slice(0, DRAFT_PREVIEW_MAX_LENGTH)}\u2026` : text;
3886
+ }
3887
+ if (draft.attachments.length > 0) {
3888
+ return draft.attachments.length === 1 ? "Attachment" : `${draft.attachments.length} attachments`;
3889
+ }
3890
+ return null;
3891
+ }
3892
+ function useConversationDraft(conversationId) {
3893
+ const composerRef = React7.useRef(emptyDraft());
3894
+ const syncComposerRef = React7.useCallback((draft) => {
3895
+ composerRef.current = draft;
3896
+ }, []);
3897
+ React7.useEffect(() => {
3898
+ const id = conversationId;
3899
+ return () => {
3900
+ if (!id) return;
3901
+ updateConversationDraft(id, composerRef.current);
3902
+ };
3903
+ }, [conversationId]);
3904
+ return {
3905
+ syncComposerRef
3906
+ };
3907
+ }
3908
+
3909
+ // src/chat/ChannelList.tsx
3785
3910
  var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSearch = "" }) => {
3786
3911
  const onlineUserIds = exports.useChatStore((s) => s.onlineUsers);
3787
3912
  const allUsers = exports.useChatStore((s) => s.allUsers);
@@ -3789,12 +3914,14 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3789
3914
  const deletedForMeIds = exports.useChatStore((s) => s.deletedForMeIds);
3790
3915
  const isFetchingUsers = exports.useChatStore((s) => s.isFetchingAllUsers);
3791
3916
  const conversations = exports.useChatStore((s) => s.conversations);
3917
+ const messagesByConversation = exports.useChatStore((s) => s.messagesByConversation);
3792
3918
  const isFetchingConversations = exports.useChatStore((s) => s.isFetchingConversations);
3793
3919
  const conversationsHasMore = exports.useChatStore((s) => s.conversationsHasMore);
3794
3920
  const conversationsPage = exports.useChatStore((s) => s.conversationsPage);
3795
3921
  const accessToken = exports.useChatStore((s) => s.accessToken);
3796
3922
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
3797
3923
  const lastFetchKeyRef = React7.useRef(null);
3924
+ useDraftRevision();
3798
3925
  React7.useEffect(() => {
3799
3926
  if (accessToken) {
3800
3927
  exports.useChatStore.getState().fetchAllUsers();
@@ -3823,17 +3950,20 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3823
3950
  const isOnline = c.conversationType === "group" || c.isGroup ? false : onlineUserIds.includes(otherParticipantId);
3824
3951
  const unreadCount = typeof c.unreadCount === "number" ? c.unreadCount : 0;
3825
3952
  const isTyping = typingUsersByConv[String(c._id)]?.length > 0;
3826
- let lastMessageContent = c.lastMessage?.content || "No messages yet";
3827
- if (c.lastMessage) {
3828
- if (c.lastMessage.isDeletedForEveryone) {
3953
+ const resolvedLastMessage = resolveConversationLastMessage(c, messagesByConversation);
3954
+ let lastMessageContent = resolvedLastMessage?.content || "No messages yet";
3955
+ if (resolvedLastMessage) {
3956
+ if (resolvedLastMessage.isDeletedForEveryone) {
3829
3957
  lastMessageContent = "This message has been deleted";
3830
- } else if (deletedForMeIds.includes(String(c.lastMessage._id || c.lastMessage.id))) {
3958
+ } else if (deletedForMeIds.includes(String(resolvedLastMessage._id || resolvedLastMessage.id))) {
3831
3959
  lastMessageContent = "";
3832
3960
  }
3833
3961
  }
3962
+ const conversationId = String(c._id);
3963
+ const draftPreview = getConversationDraftPreview(conversationId);
3834
3964
  return {
3835
- id: c.conversationType === "group" || c.isGroup ? String(c._id) : otherParticipantId,
3836
- conversationId: String(c._id),
3965
+ id: c.conversationType === "group" || c.isGroup ? conversationId : otherParticipantId,
3966
+ conversationId,
3837
3967
  name: name.charAt(0).toUpperCase() + name.slice(1),
3838
3968
  image,
3839
3969
  isOnline,
@@ -3842,16 +3972,17 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3842
3972
  isBlocked: c.isBlocked || false,
3843
3973
  unreadCount,
3844
3974
  isTyping,
3975
+ draftPreview,
3845
3976
  pinnedCount: c.pinnedCount,
3846
3977
  starredCount: c.starredCount,
3847
3978
  lastMessage: {
3848
3979
  content: lastMessageContent,
3849
- timestamp: c.lastMessage?.createdAt ? new Date(c.lastMessage.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "",
3980
+ timestamp: resolvedLastMessage?.createdAt ? new Date(resolvedLastMessage.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "",
3850
3981
  sender: "User"
3851
3982
  }
3852
3983
  };
3853
3984
  });
3854
- }, [conversations, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
3985
+ }, [conversations, messagesByConversation, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
3855
3986
  const handleLoadMore = () => {
3856
3987
  if (!isFetchingConversations && conversationsHasMore) {
3857
3988
  exports.useChatStore.getState().fetchConversations(10, conversationsPage + 1, debouncedSearch);
@@ -3882,8 +4013,10 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
3882
4013
  isActive ? "text-(--chat-theme)" : "text-[#0f172a]"
3883
4014
  ) }, channel.name), /* @__PURE__ */ React7__namespace.default.createElement("div", { className: "flex shrink-0 flex-col items-end gap-1" }, /* @__PURE__ */ React7__namespace.default.createElement("span", { className: "text-[11px] font-medium text-muted-foreground" }, channel.lastMessage?.timestamp), channel.isLeft && /* @__PURE__ */ React7__namespace.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__namespace.default.createElement("div", { className: "flex w-full min-w-0 items-center justify-between gap-2 pr-1" }, /* @__PURE__ */ React7__namespace.default.createElement("p", { className: cn(
3884
4015
  "min-w-0 line-clamp-1 text-[13px]",
3885
- channel.isTyping ? "font-medium text-(--chat-theme)" : "text-muted-foreground"
3886
- ) }, channel.isTyping ? "typing..." : channel.lastMessage?.content), channel.unreadCount !== void 0 && channel.unreadCount > 0 && /* @__PURE__ */ React7__namespace.default.createElement(Badge, { className: cn(
4016
+ channel.isTyping || channel.draftPreview ? "font-medium" : "text-muted-foreground",
4017
+ channel.isTyping && "text-(--chat-theme)",
4018
+ !channel.isTyping && channel.draftPreview && "text-muted-foreground"
4019
+ ) }, channel.isTyping ? "typing..." : channel.draftPreview ? /* @__PURE__ */ React7__namespace.default.createElement(React7__namespace.default.Fragment, null, /* @__PURE__ */ React7__namespace.default.createElement("span", { className: "font-semibold text-rose-500" }, "Draft: "), /* @__PURE__ */ React7__namespace.default.createElement("span", null, channel.draftPreview)) : channel.lastMessage?.content), channel.unreadCount !== void 0 && channel.unreadCount > 0 && /* @__PURE__ */ React7__namespace.default.createElement(Badge, { className: cn(
3887
4020
  "flex h-5 min-w-5 items-center justify-center rounded-full border-none px-1.5 text-[10px] font-semibold",
3888
4021
  isActive ? "bg-(--chat-theme) text-white" : "bg-(--chat-theme) text-white"
3889
4022
  ) }, channel.unreadCount)))
@@ -5798,10 +5931,9 @@ var MessageReactions = ({
5798
5931
  return /* @__PURE__ */ React7__namespace.default.createElement(Tooltip, { key: emoji }, /* @__PURE__ */ React7__namespace.default.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(
5799
5932
  "button",
5800
5933
  {
5801
- key: emoji,
5802
5934
  onClick: () => onReactionClick(emoji),
5803
5935
  className: cn(
5804
- "flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5",
5936
+ "flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5 outline-none",
5805
5937
  isReactedByMe ? "bg-(--chat-theme-10) border-(--chat-theme-20) text-(--chat-theme)" : "bg-white border-border/40 hover:bg-muted/50 text-[#111827]"
5806
5938
  )
5807
5939
  },
@@ -5864,13 +5996,9 @@ var MessageToolbar = ({
5864
5996
  canEdit = true
5865
5997
  }) => {
5866
5998
  return /* @__PURE__ */ React7__namespace.default.createElement("div", { className: cn(
5867
- "absolute -top-10 z-[100] flex items-center gap-1 rounded-full border bg-background/95 p-1 shadow-md backdrop-blur-sm",
5999
+ "absolute -top-10 z-20 flex items-center gap-1 rounded-full border bg-background/95 p-1 shadow-md backdrop-blur-sm",
5868
6000
  isSender ? "right-0" : "left-0"
5869
- ) }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: () => {
5870
- onReply?.();
5871
- } }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: () => {
5872
- onForward?.();
5873
- } }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Forward, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__namespace.default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__namespace.default.createElement(
6001
+ ) }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onReply }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onForward }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Forward, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__namespace.default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__namespace.default.createElement(
5874
6002
  PopoverContent,
5875
6003
  {
5876
6004
  className: "w-auto p-0 border-none shadow-2xl rounded-2xl",
@@ -6414,6 +6542,9 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6414
6542
  setState((prev) => ({ ...prev, ...updates }));
6415
6543
  };
6416
6544
  const typingTimeoutRef = React7.useRef(null);
6545
+ const conversationIdRef = React7.useRef(conversationId);
6546
+ conversationIdRef.current = conversationId;
6547
+ const { syncComposerRef } = useConversationDraft(conversationId);
6417
6548
  const emitTypingStart = exports.useChatStore((s) => s.emitTypingStart);
6418
6549
  const emitTypingStop = exports.useChatStore((s) => s.emitTypingStop);
6419
6550
  const emitGroupTypingStart = exports.useChatStore((s) => s.emitGroupTypingStart);
@@ -6425,6 +6556,30 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6425
6556
  React7__namespace.default.useEffect(() => {
6426
6557
  attachmentsCountRef.current = state.attachments.length;
6427
6558
  }, [state.attachments.length]);
6559
+ React7__namespace.default.useEffect(() => {
6560
+ const draft = {
6561
+ messageInput: state.messageInput,
6562
+ attachments: state.attachments
6563
+ };
6564
+ syncComposerRef(draft);
6565
+ updateConversationDraft(conversationIdRef.current, draft);
6566
+ }, [state.messageInput, state.attachments, syncComposerRef]);
6567
+ React7__namespace.default.useEffect(() => {
6568
+ if (typingTimeoutRef.current) {
6569
+ clearTimeout(typingTimeoutRef.current);
6570
+ typingTimeoutRef.current = null;
6571
+ }
6572
+ const draft = getConversationDraft(conversationId);
6573
+ attachmentsCountRef.current = draft.attachments.length;
6574
+ setState((prev) => ({
6575
+ ...prev,
6576
+ messageInput: draft.messageInput,
6577
+ attachments: draft.attachments,
6578
+ attachmentError: "",
6579
+ isTyping: false
6580
+ }));
6581
+ if (fileInputRef.current) fileInputRef.current.value = "";
6582
+ }, [conversationId]);
6428
6583
  const handleInputChange = (e) => {
6429
6584
  updateState({ messageInput: e.target.value });
6430
6585
  if (!conversationId || !loggedUserDetails || !activeChannel) return;
@@ -6509,6 +6664,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6509
6664
  });
6510
6665
  };
6511
6666
  const handleSendMessage = () => {
6667
+ const trimmed = state.messageInput.trim();
6668
+ if (!trimmed && state.attachments.length === 0) return;
6512
6669
  if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
6513
6670
  if (state.isTyping && conversationId && loggedUserDetails && activeChannel) {
6514
6671
  updateState({ isTyping: false });
@@ -6524,7 +6681,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
6524
6681
  emitTypingStop(typingPayload);
6525
6682
  }
6526
6683
  }
6527
- onSendMessage?.(state.messageInput.trim(), state.attachments, replyTo);
6684
+ onSendMessage?.(trimmed, state.attachments, replyTo);
6685
+ clearConversationDraft(conversationId);
6528
6686
  updateState({
6529
6687
  messageInput: "",
6530
6688
  attachments: [],
@@ -7054,6 +7212,113 @@ var ChatMain_default = ChatMain;
7054
7212
  init_useStore();
7055
7213
  init_socket_service();
7056
7214
  init_api_service();
7215
+
7216
+ // src/config/socket.config.ts
7217
+ var getSocketConfig = (accessToken, tenantId, apiBaseUrl) => {
7218
+ const baseURL = (apiBaseUrl || "").replace("/api/v1", "");
7219
+ return {
7220
+ url: baseURL,
7221
+ options: {
7222
+ path: "/connection",
7223
+ auth: {
7224
+ accessToken
7225
+ },
7226
+ query: {
7227
+ platform: "web",
7228
+ tenantId: tenantId || "",
7229
+ isTenant: "true"
7230
+ },
7231
+ extraHeaders: {
7232
+ Authorization: `Bearer ${accessToken}`,
7233
+ platform: "web",
7234
+ tenantId: tenantId || "",
7235
+ isTenant: "true"
7236
+ },
7237
+ transports: ["websocket", "polling"],
7238
+ reconnection: true,
7239
+ reconnectionAttempts: Infinity,
7240
+ reconnectionDelay: 2e3,
7241
+ reconnectionDelayMax: 5e3,
7242
+ timeout: 2e4
7243
+ }
7244
+ };
7245
+ };
7246
+ var showNotification = (message, type = "info", icon, duration) => {
7247
+ const options = {
7248
+ icon,
7249
+ duration
7250
+ };
7251
+ switch (type) {
7252
+ case "success":
7253
+ reactHotToast.toast.success(message, options);
7254
+ break;
7255
+ case "error":
7256
+ reactHotToast.toast.error(message, options);
7257
+ break;
7258
+ case "info":
7259
+ reactHotToast.toast(message, options);
7260
+ break;
7261
+ case "warning":
7262
+ reactHotToast.toast(message, { ...options, icon: "\u26A0\uFE0F" });
7263
+ break;
7264
+ default:
7265
+ reactHotToast.toast(message, options);
7266
+ }
7267
+ };
7268
+ var downloadFile = async (src, name) => {
7269
+ try {
7270
+ const link = document.createElement("a");
7271
+ link.href = src;
7272
+ link.setAttribute("download", name);
7273
+ link.setAttribute("rel", "noopener noreferrer");
7274
+ link.click();
7275
+ showNotification("File Downloaded Successfully", "success");
7276
+ } catch (err) {
7277
+ console.error("File download failed:", err);
7278
+ showNotification("Failed to download file", "error");
7279
+ }
7280
+ };
7281
+ var getResponsiveWidth = (width) => {
7282
+ const screenWidth = window.innerWidth;
7283
+ if (width) {
7284
+ return width;
7285
+ } else if (screenWidth < 640) {
7286
+ return 350;
7287
+ } else if (screenWidth >= 640 && screenWidth < 1024) {
7288
+ return 600;
7289
+ } else if (screenWidth >= 1024 && screenWidth < 1280) {
7290
+ return 800;
7291
+ } else {
7292
+ return 1e3;
7293
+ }
7294
+ };
7295
+ var isEmpty = (value) => {
7296
+ if (value === null || value === void 0) return true;
7297
+ if (typeof value === "string") return value.trim().length === 0;
7298
+ if (Array.isArray(value)) {
7299
+ return value.length === 0 || value.every((item) => isEmpty(item));
7300
+ }
7301
+ if (typeof value === "object") {
7302
+ return Object.keys(value).length === 0;
7303
+ }
7304
+ return false;
7305
+ };
7306
+ function uniqueList(list, key) {
7307
+ const seen = /* @__PURE__ */ new Set();
7308
+ return list.filter((item) => {
7309
+ const value = item[key];
7310
+ if (seen.has(value)) return false;
7311
+ seen.add(value);
7312
+ return true;
7313
+ });
7314
+ }
7315
+ var capitalizeFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
7316
+ var getFirstAndLastNameOneCharacter = (name) => {
7317
+ const names = name.split(" ");
7318
+ return names.map((name2) => name2.charAt(0).toUpperCase()).join("");
7319
+ };
7320
+
7321
+ // src/index.ts
7057
7322
  var index_default = ChatMain_default;
7058
7323
 
7059
7324
  exports.Channel = Channel;
@@ -7066,9 +7331,17 @@ exports.ForwardModal = ForwardModal_default;
7066
7331
  exports.LoggedUserDetails = LoggedUserDetails_default;
7067
7332
  exports.MessageInput = ChannelMessageBox_default;
7068
7333
  exports.MessageList = ActiveChannelMessages_default;
7334
+ exports.capitalizeFirst = capitalizeFirst;
7069
7335
  exports.default = index_default;
7070
7336
  exports.defaultChatLayoutConfig = defaultChatLayoutConfig;
7337
+ exports.downloadFile = downloadFile;
7338
+ exports.getFirstAndLastNameOneCharacter = getFirstAndLastNameOneCharacter;
7339
+ exports.getResponsiveWidth = getResponsiveWidth;
7340
+ exports.getSocketConfig = getSocketConfig;
7341
+ exports.isEmpty = isEmpty;
7071
7342
  exports.mergeChatLayoutConfig = mergeChatLayoutConfig;
7343
+ exports.showNotification = showNotification;
7344
+ exports.uniqueList = uniqueList;
7072
7345
  exports.useChatContext = useChatContext;
7073
7346
  exports.useChatController = useChatController;
7074
7347
  //# sourceMappingURL=index.cjs.map