@realtimexsco/live-chat 1.3.1 → 1.3.2

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
@@ -555,7 +555,7 @@ var init_group_display_helpers = __esm({
555
555
  });
556
556
 
557
557
  // src/store/helpers/message.helpers.ts
558
- var getMessageSenderId, extractMessageData, mergeLastMessage, messageHasPreviewBody, getLatestMessageFromList, isOwnMessage, getMessageContent, getMessageTimestamp, statusRank, preferHigherStatus, resolveMessageStatus, mergeChatMessages;
558
+ var getMessageSenderId, extractMessageData, statusRank, preferHigherStatus, mergeLastMessage, messageHasPreviewBody, getLatestMessageFromList, isOwnMessage, getMessageContent, getMessageTimestamp, resolveMessageStatus, mergeChatMessages;
559
559
  var init_message_helpers = __esm({
560
560
  "src/store/helpers/message.helpers.ts"() {
561
561
  getMessageSenderId = (message) => String(message?.sender?._id || message?.sender?.id || message?.senderId || message?.fromUserId || message?.sender || "");
@@ -570,21 +570,32 @@ var init_message_helpers = __esm({
570
570
  const caption = payload.caption ?? payload.message?.caption ?? null;
571
571
  return { messageId, content, conversationId, status, isPinned, isStarred, attachmentId, caption };
572
572
  };
573
+ statusRank = { sent: 0, delivered: 1, read: 2 };
574
+ preferHigherStatus = (base, incoming) => {
575
+ const baseRank = statusRank[base?.status] ?? 0;
576
+ const incomingRank = statusRank[incoming?.status] ?? 0;
577
+ if (incomingRank > baseRank) {
578
+ return { ...base, status: incoming.status };
579
+ }
580
+ return base;
581
+ };
573
582
  mergeLastMessage = (existing, incoming) => {
574
583
  if (!incoming) return existing ?? null;
575
584
  if (!existing) return incoming;
576
585
  const content = incoming.content?.trim() ? incoming.content : existing.content;
577
586
  const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
578
- const existingTime = new Date(existing.createdAt || existing.timestamp || 0).getTime();
579
- const incomingTime = new Date(incoming.createdAt || incoming.timestamp || 0).getTime();
580
- const newer = incomingTime >= existingTime ? incoming : existing;
587
+ const incomingSenderId = getMessageSenderId(incoming);
588
+ const resolvedSender = incomingSenderId ? incoming.sender ?? incoming.senderId ?? incoming.fromUserId : existing.sender ?? existing.senderId ?? existing.fromUserId ?? incoming.sender;
581
589
  return {
582
590
  ...existing,
583
591
  ...incoming,
584
592
  ...content !== void 0 ? { content } : {},
585
593
  ...createdAt !== void 0 ? { createdAt } : {},
586
594
  attachments: incoming.attachments?.length ? incoming.attachments : existing.attachments,
587
- status: newer.status ?? existing.status ?? incoming.status
595
+ status: preferHigherStatus(existing, incoming).status ?? existing.status ?? incoming.status,
596
+ ...resolvedSender !== void 0 ? { sender: resolvedSender } : {},
597
+ senderId: incoming.senderId ?? existing.senderId,
598
+ fromUserId: incoming.fromUserId ?? existing.fromUserId
588
599
  };
589
600
  };
590
601
  messageHasPreviewBody = (message) => Boolean(String(message?.content || "").trim()) || Array.isArray(message?.attachments) && message.attachments.length > 0;
@@ -607,15 +618,6 @@ var init_message_helpers = __esm({
607
618
  const time = value ? new Date(value).getTime() : NaN;
608
619
  return Number.isFinite(time) ? time : 0;
609
620
  };
610
- statusRank = { sent: 0, delivered: 1, read: 2 };
611
- preferHigherStatus = (base, incoming) => {
612
- const baseRank = statusRank[base?.status] ?? 0;
613
- const incomingRank = statusRank[incoming?.status] ?? 0;
614
- if (incomingRank > baseRank) {
615
- return { ...base, status: incoming.status };
616
- }
617
- return base;
618
- };
619
621
  resolveMessageStatus = (message, conversation, loggedUserId) => {
620
622
  const existing = message?.status || "sent";
621
623
  if (!loggedUserId || !isOwnMessage(message, loggedUserId)) return existing;
@@ -790,7 +792,7 @@ var init_unread_helpers = __esm({
790
792
  });
791
793
 
792
794
  // src/store/helpers/conversation.helpers.ts
793
- var findConversationIdByMessageId, isGroupConversation, normalizeConversationRecord, resolveConversationLastMessage, syncConversationLastMessage, upsertConversationInList, findConversationById, findConversationForChannel, resolveConversationIdForChannel;
795
+ var findConversationIdByMessageId, applyMessageStatusToStore, isGroupConversation, normalizeConversationRecord, resolveConversationLastMessage, syncConversationLastMessage, upsertConversationInList, findConversationById, findConversationIdByLastMessageId, findConversationForChannel, resolveConversationIdForChannel;
794
796
  var init_conversation_helpers = __esm({
795
797
  "src/store/helpers/conversation.helpers.ts"() {
796
798
  init_group_display_helpers();
@@ -804,6 +806,40 @@ var init_conversation_helpers = __esm({
804
806
  )
805
807
  );
806
808
  };
809
+ applyMessageStatusToStore = (state, messageId, status, conversationId) => {
810
+ const normalizedMessageId = String(messageId);
811
+ const resolvedConversationId = conversationId ? String(conversationId) : findConversationIdByMessageId(state.messagesByConversation, normalizedMessageId) || findConversationIdByLastMessageId(state.conversations, normalizedMessageId) || null;
812
+ const nextConversations = state.conversations.map((conversation) => {
813
+ const lastMessageId = String(
814
+ conversation?.lastMessage?._id || conversation?.lastMessage?.id || ""
815
+ );
816
+ if (lastMessageId !== normalizedMessageId) return conversation;
817
+ return {
818
+ ...conversation,
819
+ lastMessage: mergeLastMessage(conversation.lastMessage, { status })
820
+ };
821
+ });
822
+ const nextMessagesByConversation = { ...state.messagesByConversation };
823
+ const patchMessages = (messages) => messages.map(
824
+ (message) => String(message._id || message.id) === normalizedMessageId ? preferHigherStatus(message, { status }) : message
825
+ );
826
+ if (resolvedConversationId && nextMessagesByConversation[resolvedConversationId]) {
827
+ nextMessagesByConversation[resolvedConversationId] = patchMessages(
828
+ nextMessagesByConversation[resolvedConversationId]
829
+ );
830
+ } else {
831
+ for (const cid of Object.keys(nextMessagesByConversation)) {
832
+ const patched = patchMessages(nextMessagesByConversation[cid]);
833
+ if (patched !== nextMessagesByConversation[cid]) {
834
+ nextMessagesByConversation[cid] = patched;
835
+ }
836
+ }
837
+ }
838
+ return {
839
+ conversations: nextConversations,
840
+ messagesByConversation: nextMessagesByConversation
841
+ };
842
+ };
807
843
  isGroupConversation = (conversation) => {
808
844
  if (!conversation) return false;
809
845
  return conversation.conversationType === "group" || !!conversation.isGroup;
@@ -893,6 +929,14 @@ var init_conversation_helpers = __esm({
893
929
  if (!conversationId) return void 0;
894
930
  return conversations.find((c) => String(c._id) === String(conversationId));
895
931
  };
932
+ findConversationIdByLastMessageId = (conversations, messageId) => {
933
+ if (!messageId) return null;
934
+ const normalizedMessageId = String(messageId);
935
+ const match = conversations.find(
936
+ (conversation) => String(conversation?.lastMessage?._id || conversation?.lastMessage?.id || "") === normalizedMessageId
937
+ );
938
+ return match?._id ? String(match._id) : null;
939
+ };
896
940
  findConversationForChannel = (channel, loggedUserId, conversations) => {
897
941
  if (!channel || !loggedUserId) return void 0;
898
942
  if (channel.conversationId) {
@@ -2965,28 +3009,16 @@ var init_dm_actions = __esm({
2965
3009
  },
2966
3010
  receivedStatusUpdate: (payload) => {
2967
3011
  const data = extractMessageData(payload);
2968
- let { conversationId } = data;
2969
3012
  const { messageId, status } = data;
2970
- if (!messageId) return;
3013
+ let { conversationId } = data;
3014
+ if (!messageId || !status) return;
2971
3015
  set({ lastStatusUpdate: payload });
2972
3016
  if (!conversationId) {
2973
- conversationId = findConversationIdByMessageId(get().messagesByConversation, messageId);
3017
+ conversationId = findConversationIdByMessageId(get().messagesByConversation, messageId) || findConversationIdByLastMessageId(get().conversations, messageId) || void 0;
2974
3018
  }
2975
- if (!conversationId || !status) return;
2976
- set((state) => {
2977
- const messages = state.messagesByConversation[conversationId] || [];
2978
- return {
2979
- messagesByConversation: {
2980
- ...state.messagesByConversation,
2981
- [conversationId]: messages.map(
2982
- (m) => String(m._id || m.id) === String(messageId) ? preferHigherStatus(m, { status }) : m
2983
- )
2984
- },
2985
- conversations: state.conversations.map(
2986
- (c) => String(c._id) === String(conversationId) && c.lastMessage && String(c.lastMessage._id || c.lastMessage.id) === String(messageId) ? { ...c, lastMessage: mergeLastMessage(c.lastMessage, { status }) } : c
2987
- )
2988
- };
2989
- });
3019
+ set((state) => ({
3020
+ ...applyMessageStatusToStore(state, String(messageId), status, conversationId)
3021
+ }));
2990
3022
  },
2991
3023
  receivedDMMarkRead: (payload) => {
2992
3024
  const { conversationId } = payload;
@@ -3247,25 +3279,14 @@ var init_group_actions2 = __esm({
3247
3279
  let { conversationId } = data;
3248
3280
  const { messageId, status } = data;
3249
3281
  set({ lastStatusUpdate: payload });
3282
+ if (!status) return;
3250
3283
  if (messageId) {
3251
3284
  if (!conversationId) {
3252
- conversationId = findConversationIdByMessageId(get().messagesByConversation, messageId);
3285
+ conversationId = findConversationIdByMessageId(get().messagesByConversation, messageId) || findConversationIdByLastMessageId(get().conversations, messageId) || void 0;
3253
3286
  }
3254
- if (!conversationId) return;
3255
- set((state) => {
3256
- const messages = state.messagesByConversation[conversationId] || [];
3257
- return {
3258
- messagesByConversation: {
3259
- ...state.messagesByConversation,
3260
- [conversationId]: messages.map(
3261
- (m) => String(m._id || m.id) === String(messageId) ? { ...m, status } : m
3262
- )
3263
- },
3264
- conversations: state.conversations.map(
3265
- (c) => String(c._id) === String(conversationId) && c.lastMessage && String(c.lastMessage._id || c.lastMessage.id) === String(messageId) ? { ...c, lastMessage: { ...c.lastMessage, status } } : c
3266
- )
3267
- };
3268
- });
3287
+ set((state) => ({
3288
+ ...applyMessageStatusToStore(state, String(messageId), status, conversationId)
3289
+ }));
3269
3290
  return;
3270
3291
  }
3271
3292
  if (conversationId && status === "read") {
@@ -3280,7 +3301,7 @@ var init_group_actions2 = __esm({
3280
3301
  if (String(c._id) !== String(conversationId)) return c;
3281
3302
  let nextLastMessage = c.lastMessage;
3282
3303
  if (nextLastMessage && String(nextLastMessage.sender?._id || nextLastMessage.sender) === String(myId)) {
3283
- nextLastMessage = { ...nextLastMessage, status: "read" };
3304
+ nextLastMessage = mergeLastMessage(nextLastMessage, { status: "read" });
3284
3305
  }
3285
3306
  return { ...c, lastMessage: nextLastMessage };
3286
3307
  });
@@ -6795,15 +6816,13 @@ function useChannelList({ debouncedSearch = "" }) {
6795
6816
  }
6796
6817
  }
6797
6818
  const draftPreview = getConversationDraftPreview(conversationId);
6798
- const lastMsgSenderId = String(
6799
- resolvedLastMessage?.sender?._id || resolvedLastMessage?.sender || ""
6800
- );
6801
- const isOwnLastMessage = lastMsgSenderId === String(loggedUserDetails?._id);
6802
- const lastMessageStatus = resolveMessageStatus(
6803
- resolvedLastMessage,
6804
- c,
6805
- loggedUserDetails?._id
6806
- );
6819
+ const storedLastMessage = c.lastMessage;
6820
+ const storedLastMessageId = String(storedLastMessage?._id || storedLastMessage?.id || "");
6821
+ const resolvedLastMessageId = String(resolvedLastMessage?._id || resolvedLastMessage?.id || "");
6822
+ const lastMessageStatus = storedLastMessageId && resolvedLastMessageId && storedLastMessageId === resolvedLastMessageId ? preferHigherStatus(
6823
+ { status: storedLastMessage?.status },
6824
+ { status: resolvedLastMessage?.status }
6825
+ ).status ?? storedLastMessage?.status ?? resolvedLastMessage?.status : storedLastMessage?.status;
6807
6826
  return {
6808
6827
  id: isGroup ? conversationId : otherParticipantId,
6809
6828
  conversationId,
@@ -6817,12 +6836,11 @@ function useChannelList({ debouncedSearch = "" }) {
6817
6836
  unreadCount,
6818
6837
  isTyping,
6819
6838
  draftPreview,
6820
- isOwnLastMessage,
6821
6839
  pinnedCount: c.pinnedCount,
6822
6840
  starredCount: c.starredCount,
6823
6841
  lastMessage: {
6824
6842
  content: lastMessageContent,
6825
- timestamp: resolvedLastMessage?.createdAt ? formatSidebarTime(resolvedLastMessage.createdAt) : "",
6843
+ timestamp: resolvedLastMessage?.createdAt ? formatSidebarTime(resolvedLastMessage.createdAt) : c.lastMessage?.createdAt ? formatSidebarTime(c.lastMessage.createdAt) : "",
6826
6844
  status: lastMessageStatus,
6827
6845
  sender: "User"
6828
6846
  }
@@ -6851,7 +6869,7 @@ function useChannelList({ debouncedSearch = "" }) {
6851
6869
  init_store_helpers();
6852
6870
  function ChannelListItem({ channel, activeChannel, onChannelSelect }) {
6853
6871
  const isActive = activeChannel?.conversationId ? String(activeChannel.conversationId) === String(channel.conversationId) : activeChannel?.id === channel.id;
6854
- const showReadReceipt = channel.isOwnLastMessage && !channel.unreadCount && !channel.isTyping && !channel.draftPreview;
6872
+ const showReadReceipt = !!channel.lastMessage?.status && !channel.unreadCount && !channel.isTyping && !channel.draftPreview;
6855
6873
  const lastStatus = channel.lastMessage?.status;
6856
6874
  return /* @__PURE__ */ jsxs(
6857
6875
  "div",
@@ -8520,24 +8538,11 @@ function GroupSenderName({ name, className }) {
8520
8538
  }
8521
8539
 
8522
8540
  // src/chat/lib/chat-message-bubble.ts
8523
- function getBubbleCornerClasses(isSender, isGroupedWithPrev, isGroupedWithNext) {
8524
- if (isSender) {
8525
- return cn(
8526
- "rounded-tl-[18px] rounded-bl-[18px]",
8527
- isGroupedWithPrev ? "rounded-tr-[6px]" : "rounded-tr-[18px]",
8528
- isGroupedWithNext ? "rounded-br-[6px]" : "rounded-br-[4px]"
8529
- );
8530
- }
8531
- return cn(
8532
- "rounded-tr-[18px] rounded-br-[18px]",
8533
- isGroupedWithPrev ? "rounded-tl-[6px]" : "rounded-tl-[18px]",
8534
- isGroupedWithNext ? "rounded-bl-[6px]" : "rounded-bl-[4px]"
8535
- );
8541
+ function getBubbleCornerClasses() {
8542
+ return "rounded-tl-[18px] rounded-tr-[18px] rounded-br-[18px] rounded-bl-none";
8536
8543
  }
8537
8544
  function getChatBubbleClasses({
8538
8545
  isSender = false,
8539
- isGroupedWithPrev = false,
8540
- isGroupedWithNext = false,
8541
8546
  surface,
8542
8547
  className
8543
8548
  }) {
@@ -8545,7 +8550,7 @@ function getChatBubbleClasses({
8545
8550
  const isNeutral = resolvedSurface === "neutral";
8546
8551
  return cn(
8547
8552
  "relative",
8548
- getBubbleCornerClasses(isSender, isGroupedWithPrev, isGroupedWithNext),
8553
+ getBubbleCornerClasses(),
8549
8554
  resolvedSurface === "sent" && "chat-bubble-sent bg-(--chat-theme) text-white",
8550
8555
  resolvedSurface === "received" && "chat-bubble-received border border-(--chat-border)/60 bg-(--chat-incoming-bubble) text-(--chat-incoming-text)",
8551
8556
  isNeutral && "chat-bubble-received border border-(--chat-border)/60 bg-(--chat-incoming-bubble) text-(--chat-incoming-text)",
@@ -10360,8 +10365,6 @@ function MessageAttachmentsView({
10360
10365
  "relative w-full",
10361
10366
  isBubbleLayout && getChatBubbleClasses({
10362
10367
  isSender,
10363
- isGroupedWithPrev,
10364
- isGroupedWithNext,
10365
10368
  surface: bubbleSurface,
10366
10369
  className: cn(
10367
10370
  isImageOnlyBubble ? "p-0" : isForwarded ? "px-2 py-0.5" : "px-2.5 py-1"
@@ -13822,8 +13825,6 @@ function MessageItemTextBubble({
13822
13825
  }) {
13823
13826
  const bubbleClasses = (extra) => getChatBubbleClasses({
13824
13827
  isSender,
13825
- isGroupedWithPrev,
13826
- isGroupedWithNext,
13827
13828
  className: extra
13828
13829
  });
13829
13830
  return /* @__PURE__ */ jsxs(
@@ -14116,8 +14117,6 @@ function MessageItemView(props) {
14116
14117
  const showMessageSelectionRadio = selectionMode && !isDeletedForEveryone && !!mid && !hasMultiAttachments;
14117
14118
  const bubbleClasses = (extra) => getChatBubbleClasses({
14118
14119
  isSender,
14119
- isGroupedWithPrev,
14120
- isGroupedWithNext,
14121
14120
  className: extra
14122
14121
  });
14123
14122
  return /* @__PURE__ */ jsxs(
@@ -16871,7 +16870,7 @@ var DefaultChannelListItem = ({
16871
16870
  onSelect
16872
16871
  }) => {
16873
16872
  const { slotClassName } = useChatCustomizationOptional();
16874
- const showReadReceipt = channel.isOwnLastMessage && !channel.unreadCount && !channel.isTyping && !channel.draftPreview;
16873
+ const showReadReceipt = !!channel.lastMessage?.status && !channel.unreadCount && !channel.isTyping && !channel.draftPreview;
16875
16874
  const lastStatus = channel.lastMessage?.status;
16876
16875
  return /* @__PURE__ */ jsxs(
16877
16876
  "div",