@droppii-org/chat-sdk 0.6.41 → 0.6.42

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationDetailHeader.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailHeader.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAQrE,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAsBD,QAAA,MAAM,wBAAwB,GAAI,2CAI/B,6BAA6B,4CA2C/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
1
+ {"version":3,"file":"ConversationDetailHeader.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailHeader.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,gBAAgB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAsBD,QAAA,MAAM,wBAAwB,GAAI,2CAI/B,6BAA6B,4CAyC/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -4,15 +4,11 @@ import { useTranslation } from "react-i18next";
4
4
  import { Icon } from "../../components/icon";
5
5
  import ConversationPeerAvatar from "../../components/conversation-inbox/ConversationPeerAvatar";
6
6
  import ConversationInboxTitle from "../../components/conversation-inbox/ConversationInboxTitle";
7
- import { resolveConversationDisplayName, resolvePeerBadgeLabel, } from "../../utils/conversation";
8
- import { useConversationAvatarUrl } from "../../hooks/conversation/useConversationAvatarUrl";
7
+ import { useConversationPeerIdentity } from "../../hooks/conversation/useConversationPeerIdentity";
9
8
  const HeaderIconButton = ({ icon, onClick, testId, }) => (_jsx("button", { type: "button", onClick: onClick, "data-testid": testId, "data-cp-name": "HeaderIconButton", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-[var(--dc-color-nav-icon)]", children: _jsx(Icon, { icon: icon, size: 24 }) }));
10
9
  const ConversationDetailHeader = ({ conversation, onBack, onProfileClick, }) => {
11
10
  const { t } = useTranslation("conversation-inbox");
12
- const displayName = resolveConversationDisplayName(conversation, t);
13
- const avatarUrl = useConversationAvatarUrl(conversation);
14
- const peerType = conversation.peerType;
15
- const badgeLabel = resolvePeerBadgeLabel(peerType, t);
11
+ const { displayName, avatarUrl, peerType, badgeLabel } = useConversationPeerIdentity(conversation);
16
12
  const subtitle = t("thread_detail.profile_hint");
17
13
  return (_jsxs("div", { "data-testid": "conversation-detail-header", "data-cp-name": "ConversationDetailHeader", className: "flex w-full cursor-default items-center border-b border-gray-25 bg-white p-2 shadow-inset-gray-divider", children: [_jsx(HeaderIconButton, { icon: "arrow-left-o", onClick: onBack, testId: "conversation-detail-back" }), _jsxs("div", { className: "flex min-w-0 flex-1 cursor-pointer items-center gap-2 px-1", "data-testid": "conversation-detail-profile", onClick: onProfileClick, children: [_jsx(ConversationPeerAvatar, { peerType: peerType, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(ConversationInboxTitle, { peerType: peerType, displayName: displayName }), _jsx("p", { className: "truncate text-[10px] font-normal leading-[160%] tracking-[0.1px] text-gray-600 [font-feature-settings:'liga'_off,'clig'_off]", children: subtitle })] })] })] }));
18
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,4DAI9B,4BAA4B,4CA8N9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAsBzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,4DAI9B,4BAA4B,4CAsO9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -13,7 +13,7 @@ import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
13
13
  import { useGroupPermission } from "../../hooks/group/useGroupPermission";
14
14
  import useConversationStore from "../../store/conversation";
15
15
  import useAuthStore from "../../store/auth";
16
- import { isGroupChat as resolveIsGroupChat } from "../../utils/conversation";
16
+ import { isGroupChat as resolveIsGroupChat, isSupportSessionAgentView, } from "../../utils/conversation";
17
17
  import emitter from "../../utils/events";
18
18
  import MessageItem from "../../components/message/item";
19
19
  import PinLimitModal from "./PinLimitModal";
@@ -33,8 +33,12 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
33
33
  // Some hosts run pin without their backend returning PIN_MESSAGE yet, so
34
34
  // the permission check would hide a feature that is live for them.
35
35
  const canPinMessage = !isGroupChat || permissions.pinMessage || allowPinMessageWithoutPermission;
36
- const messageController = useMessage(conversationId, searchClientMsgID, true, true, onSearchMessageRevoked);
37
- const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottomIfAtBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: (user === null || user === void 0 ? void 0 : user.userID) || authUserID, isGroupChat }, messageController));
36
+ const viewerUserID = (user === null || user === void 0 ? void 0 : user.userID) || authUserID;
37
+ // App history strips the assignment log and session dividers, which an
38
+ // agent handling someone else's session needs — load the full history.
39
+ const useAppHistory = !isSupportSessionAgentView(Object.assign(Object.assign({}, conversation), { conversationID: conversationId }), viewerUserID);
40
+ const messageController = useMessage(conversationId, searchClientMsgID, true, useAppHistory, onSearchMessageRevoked);
41
+ const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottomIfAtBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: viewerUserID, isGroupChat }, messageController));
38
42
  const { loadState, moreOldLoading } = messageController;
39
43
  const isMessageLoading = !conversationId || loadState.initLoading;
40
44
  const [openToolboxMessageId, setOpenToolboxMessageId] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAc7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACzD;AAED,QAAA,MAAM,kBAAkB,GAAI,oDAKzB,uBAAuB,4CA8GzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAU7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACzD;AAED,QAAA,MAAM,kBAAkB,GAAI,oDAKzB,uBAAuB,4CA4GzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -6,22 +6,19 @@ import { useTranslation } from "react-i18next";
6
6
  import { Icon } from "../../components/icon";
7
7
  import MediaEntryCard from "../../components/media-resources/MediaEntryCard";
8
8
  import { PeerType } from "../../types/conversation";
9
- import { resolveConversationDisplayName, resolvePeerBadgeColor, resolvePeerBadgeLabel, } from "../../utils/conversation";
10
- import { useConversationAvatarUrl } from "../../hooks/conversation/useConversationAvatarUrl";
9
+ import { resolvePeerBadgeColor } from "../../utils/conversation";
10
+ import { useConversationPeerIdentity } from "../../hooks/conversation/useConversationPeerIdentity";
11
11
  import { useLeaveGroupAction } from "../../hooks/group/useLeaveGroupAction";
12
12
  import LeaveGroupBlockedModal from "./LeaveGroupBlockedModal";
13
13
  import LeaveGroupConfirmModal from "./LeaveGroupConfirmModal";
14
14
  const PROFILE_AVATAR_SIZE = 96;
15
15
  const ThreadProfilePanel = ({ open, conversation, onClose, onSelectResource, }) => {
16
16
  const { t } = useTranslation("conversation-inbox");
17
- const displayName = resolveConversationDisplayName(conversation, t);
18
- const avatarUrl = useConversationAvatarUrl(conversation);
19
- const peerType = conversation.peerType;
20
- const badgeLabel = resolvePeerBadgeLabel(peerType, t);
17
+ const { displayName, avatarUrl, peerType, badgeLabel } = useConversationPeerIdentity(conversation);
21
18
  const { canLeaveGroup, isPublicGroup, isLeaving, isConfirmOpen, isBlockedOpen, openLeaveGroup, confirmLeaveGroup, closeConfirm, closeBlocked, } = useLeaveGroupAction({
22
19
  groupID: conversation.groupID,
23
20
  conversationID: conversation.conversationID,
24
- isGroupThread: peerType === PeerType.GROUP,
21
+ isGroupThread: conversation.peerType === PeerType.GROUP,
25
22
  onLeft: onClose,
26
23
  });
27
24
  return (_jsxs(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", "data-cp-name": "ThreadProfilePanel", styles: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AAyCtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAkCD,QAAA,MAAM,WAAW,GAAI,0PAgBlB,gBAAgB,mDA2WlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AA0CtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAkCD,QAAA,MAAM,WAAW,GAAI,0PAgBlB,gBAAgB,mDAoXlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -22,6 +22,7 @@ import SystemLogMessageItem from "./SystemLogMessage";
22
22
  import GroupNotificationMessageItem from "./GroupNotificationMessage";
23
23
  import SessionEventMessageItem from "./SessionEventMessage";
24
24
  import { isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType, } from "../../../utils/messageLog";
25
+ import { isSupportSessionAgentView } from "../../../utils/conversation";
25
26
  import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessageType, isPinnableMessageType, MessageToolboxActionKey, MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, useMessageToolboxVisibility, } from "../../../components/message/toolbox";
26
27
  const MESSAGE_AVATAR_SIZE = 24;
27
28
  const DIVIDER_GAP_MINUTES = 30;
@@ -148,6 +149,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
148
149
  const isSystemLogMessage = isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType);
149
150
  const isGroupNotificationMessage = isGroupNotificationMessageType(message === null || message === void 0 ? void 0 : message.contentType);
150
151
  const isSessionEvent = isSessionEventMessage(message);
152
+ const showsSessionEvents = isCrm || isSupportSessionAgentView(conversationData, userID);
151
153
  const isStickerMessage = (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.StickerMessage;
152
154
  const hasContextMenu = contextMenuItems.length > 0;
153
155
  const isLatestMine = ((_a = allMessages.find((m) => m.sendID === userID)) === null || _a === void 0 ? void 0 : _a.clientMsgID) ===
@@ -165,7 +167,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
165
167
  // apps get an unexplained gap where the hidden session divider used to be.
166
168
  const breaksGroup = (neighbour) => isSystemLogMessageType(neighbour.contentType) ||
167
169
  isGroupNotificationMessageType(neighbour.contentType) ||
168
- (isSessionEventMessage(neighbour) && isCrm);
170
+ (isSessionEventMessage(neighbour) && showsSessionEvents);
169
171
  const prevBreaksGroup = immediatePrev ? breaksGroup(immediatePrev) : false;
170
172
  const nextBreaksGroup = immediateNext ? breaksGroup(immediateNext) : false;
171
173
  const prevSameUser = !isOrder &&
@@ -189,7 +191,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
189
191
  const showAvatar = showPeerInfo && !isMine && !nextSameUser;
190
192
  const showSenderName = showPeerInfo && !isMine && !prevSameUser;
191
193
  const showTip = forceHideTip ? false : !nextSameUser;
192
- if (isMessageHiddenForViewer(message, isCrm, conversationData !== null && conversationData !== void 0 ? conversationData : undefined)) {
194
+ if (isMessageHiddenForViewer(message, isCrm, conversationData !== null && conversationData !== void 0 ? conversationData : undefined, userID)) {
193
195
  return null;
194
196
  }
195
197
  const messageContainerProps = {
@@ -208,8 +210,8 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
208
210
  _jsx(GroupNotificationMessageItem, { message: message })));
209
211
  }
210
212
  if (isSessionEvent) {
211
- // Consumer apps hide session dividers — see isSessionEventMessage.
212
- if (!isCrm)
213
+ // Owners in consumer apps don't see session dividers; CRM and agents do.
214
+ if (!showsSessionEvents)
213
215
  return null;
214
216
  return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
215
217
  timeBreakNode,
@@ -0,0 +1,15 @@
1
+ import type { ConversationItem } from "@droppii-org/wasm-client-sdk";
2
+ import { PeerType } from "../../types/conversation";
3
+ export interface ConversationPeerIdentity {
4
+ displayName: string;
5
+ avatarUrl: string | null;
6
+ peerType: PeerType;
7
+ badgeLabel: string;
8
+ }
9
+ /**
10
+ * Who the thread header/profile should present. A support group's
11
+ * showName/faceURL is the bot's, so an agent viewing someone else's session
12
+ * sees the session owner instead.
13
+ */
14
+ export declare const useConversationPeerIdentity: (conversation: ConversationItem) => ConversationPeerIdentity;
15
+ //# sourceMappingURL=useConversationPeerIdentity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationPeerIdentity.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useConversationPeerIdentity.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAShD,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,cAAc,gBAAgB,KAC7B,wBAsCF,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { useEffect } from "react";
3
+ import { useTranslation } from "react-i18next";
4
+ import { DChatSDK } from "../../constants/sdk";
5
+ import useAuthStore from "../../store/auth";
6
+ import useUsersInfoStore from "../../store/usersInfo";
7
+ import { PeerType } from "../../types/conversation";
8
+ import { isSupportSessionAgentView, resolveConversationDisplayName, resolvePeerBadgeLabel, resolveSupportSessionOwnerId, } from "../../utils/conversation";
9
+ import { useConversationAvatarUrl } from "./useConversationAvatarUrl";
10
+ /**
11
+ * Who the thread header/profile should present. A support group's
12
+ * showName/faceURL is the bot's, so an agent viewing someone else's session
13
+ * sees the session owner instead.
14
+ */
15
+ export const useConversationPeerIdentity = (conversation) => {
16
+ var _a, _b;
17
+ const { t } = useTranslation("conversation-inbox");
18
+ const userID = useAuthStore((state) => state.userID);
19
+ const conversationAvatarUrl = useConversationAvatarUrl(conversation);
20
+ const ownerId = isSupportSessionAgentView(conversation, userID)
21
+ ? resolveSupportSessionOwnerId(conversation)
22
+ : "";
23
+ const owner = useUsersInfoStore((state) => ownerId ? state.usersInfo[ownerId] : undefined);
24
+ useEffect(() => {
25
+ if (!ownerId || owner)
26
+ return;
27
+ DChatSDK.getUsersInfo([ownerId])
28
+ .then(({ data }) => useUsersInfoStore.getState().upsertUsers(data))
29
+ .catch((error) => {
30
+ console.error("Error fetching session owner info:", error);
31
+ });
32
+ }, [ownerId, owner]);
33
+ if (ownerId) {
34
+ return {
35
+ displayName: ((_a = owner === null || owner === void 0 ? void 0 : owner.nickname) === null || _a === void 0 ? void 0 : _a.trim()) || t("thread_card.fallback_name"),
36
+ avatarUrl: ((_b = owner === null || owner === void 0 ? void 0 : owner.faceURL) === null || _b === void 0 ? void 0 : _b.trim()) || null,
37
+ peerType: PeerType.USER,
38
+ badgeLabel: "",
39
+ };
40
+ }
41
+ const peerType = conversation.peerType;
42
+ return {
43
+ displayName: resolveConversationDisplayName(conversation, t),
44
+ avatarUrl: conversationAvatarUrl,
45
+ peerType,
46
+ badgeLabel: resolvePeerBadgeLabel(peerType, t),
47
+ };
48
+ };
@@ -3,7 +3,8 @@ export declare const visibleTypeMessage: MessageType[];
3
3
  export declare const isMessageHiddenForViewer: (message: MessageItem, isCrm: boolean, conversation?: {
4
4
  peerType?: string;
5
5
  conversationType?: number;
6
- }) => boolean;
6
+ ex?: string;
7
+ }, viewerUserID?: string) => boolean;
7
8
  export declare const useMessage: (conversationId?: string, searchClientMsgID?: string, enabled?: boolean, useAppHistory?: boolean, onSearchMessageRevoked?: () => void) => {
8
9
  loadState: {
9
10
  initLoading: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAetC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9D,OAqBF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAgBd,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;CAwUnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAkBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC"}
1
+ {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAgBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA0BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAgBd,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;CAoVnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAkBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC"}
@@ -10,9 +10,10 @@ import isEmpty from "lodash/isEmpty";
10
10
  import { useChatContext } from "../../context/ChatContext";
11
11
  import useAuthStore from "../../store/auth";
12
12
  import { ConnectStatus, SyncStatus } from "../../types/chat";
13
- import { GROUP_NOTIFICATION_MESSAGE_TYPES, isGroupNotificationMessageType, isSystemLogMessageType } from "../../utils/messageLog";
13
+ import { GROUP_NOTIFICATION_MESSAGE_TYPES, isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType } from "../../utils/messageLog";
14
14
  import { isRenderableCustomMessage } from "../../utils/customMessage";
15
15
  import { isGroupConversation } from "../../utils/imCommon";
16
+ import { isSupportSessionAgentView } from "../../utils/conversation";
16
17
  export const visibleTypeMessage = [
17
18
  MessageType.TextMessage,
18
19
  MessageType.PictureMessage,
@@ -32,7 +33,7 @@ export const visibleTypeMessage = [
32
33
  MessageType.LogTextMessage,
33
34
  ...GROUP_NOTIFICATION_MESSAGE_TYPES,
34
35
  ];
35
- export const isMessageHiddenForViewer = (message, isCrm, conversation) => {
36
+ export const isMessageHiddenForViewer = (message, isCrm, conversation, viewerUserID) => {
36
37
  if (!visibleTypeMessage.includes(message === null || message === void 0 ? void 0 : message.contentType))
37
38
  return true;
38
39
  // Group notification messages stay hidden in a CX support session even
@@ -43,9 +44,14 @@ export const isMessageHiddenForViewer = (message, isCrm, conversation) => {
43
44
  }
44
45
  if (isCrm)
45
46
  return false;
47
+ // An agent handling someone else's session (e.g. PDP CS) needs the
48
+ // assignment log and session dividers; the owner never sees them.
49
+ const isAgentView = isSupportSessionAgentView(conversation, viewerUserID);
50
+ if (isAgentView && isSessionEventMessage(message))
51
+ return false;
46
52
  const isCustomMessage = MessageType.CustomMessage === (message === null || message === void 0 ? void 0 : message.contentType);
47
53
  return (Boolean(message === null || message === void 0 ? void 0 : message.isInternal) ||
48
- isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType) ||
54
+ (!isAgentView && isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType)) ||
49
55
  (isCustomMessage && !isRenderableCustomMessage(message)));
50
56
  };
51
57
  export const useMessage = (conversationId, searchClientMsgID, enabled = true, useAppHistory = false,
@@ -69,6 +75,11 @@ onSearchMessageRevoked) => {
69
75
  const latestLoadState = useLatest(loadState);
70
76
  const canLoadRef = useLatest(canLoad);
71
77
  const conversationIdRef = useLatest(conversationId);
78
+ const useAppHistoryRef = useLatest(useAppHistory);
79
+ // The load effect resets the list when the conversation or history mode
80
+ // changes; a response still in flight for the old one must not merge in.
81
+ const isStaleResponse = (reqConversationID, reqUseAppHistory) => conversationIdRef.current !== reqConversationID ||
82
+ useAppHistoryRef.current !== reqUseAppHistory;
72
83
  // searchMessages can run more than once for a single user action — React
73
84
  // StrictMode double-invokes the effect below in dev, and any change to its
74
85
  // other deps (canLoad flipping as the SDK connects, userID arriving) re-runs
@@ -79,6 +90,7 @@ onSearchMessageRevoked) => {
79
90
  const { loading: moreOldLoading, runAsync: getMoreOldMessages } = useRequest(async (loadMore = true) => {
80
91
  var _a, _b, _c, _d, _e;
81
92
  const reqConversationID = conversationId;
93
+ const reqUseAppHistory = useAppHistory;
82
94
  const params = {
83
95
  count: PAGE_SIZE.large,
84
96
  startClientMsgID: loadMore
@@ -91,10 +103,10 @@ onSearchMessageRevoked) => {
91
103
  const { data } = useAppHistory
92
104
  ? await DChatSDK.getAdvancedHistoryMessageListApp(params)
93
105
  : await DChatSDK.getAdvancedHistoryMessageList(params);
94
- if (conversationIdRef.current !== reqConversationID)
106
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
95
107
  return;
96
108
  setTimeout(() => {
97
- if (conversationIdRef.current !== reqConversationID)
109
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
98
110
  return;
99
111
  setLoadState((preState) => {
100
112
  // Always merge with preState (never discard it, even for the
@@ -109,7 +121,7 @@ onSearchMessageRevoked) => {
109
121
  });
110
122
  }
111
123
  catch (_f) {
112
- if (conversationIdRef.current !== reqConversationID)
124
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
113
125
  return;
114
126
  setLoadState((preState) => (Object.assign(Object.assign({}, preState), { initLoading: false })));
115
127
  }
@@ -119,6 +131,7 @@ onSearchMessageRevoked) => {
119
131
  const { loading: moreNewLoading, runAsync: getMoreNewMessages } = useRequest(async (loadMore = true) => {
120
132
  var _a, _b;
121
133
  const reqConversationID = conversationId;
134
+ const reqUseAppHistory = useAppHistory;
122
135
  try {
123
136
  const reverseParams = {
124
137
  count: PAGE_SIZE.large,
@@ -131,10 +144,10 @@ onSearchMessageRevoked) => {
131
144
  const { data } = useAppHistory
132
145
  ? await DChatSDK.getAdvancedHistoryMessageListReverseApp(reverseParams)
133
146
  : await DChatSDK.getAdvancedHistoryMessageListReverse(reverseParams);
134
- if (conversationIdRef.current !== reqConversationID)
147
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
135
148
  return;
136
149
  setTimeout(() => {
137
- if (conversationIdRef.current !== reqConversationID)
150
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
138
151
  return;
139
152
  setLoadState((preState) => {
140
153
  let messageList = [
@@ -147,7 +160,7 @@ onSearchMessageRevoked) => {
147
160
  });
148
161
  }
149
162
  catch (_c) {
150
- if (conversationIdRef.current !== reqConversationID)
163
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
151
164
  return;
152
165
  setLoadState((preState) => (Object.assign(Object.assign({}, preState), { initLoading: false })));
153
166
  }
@@ -159,6 +172,7 @@ onSearchMessageRevoked) => {
159
172
  if (!searchClientMsgID || !conversationId)
160
173
  return;
161
174
  const reqConversationID = conversationId;
175
+ const reqUseAppHistory = useAppHistory;
162
176
  const searchPrevParams = {
163
177
  count: PAGE_SIZE.small,
164
178
  startClientMsgID: searchClientMsgID,
@@ -184,7 +198,7 @@ onSearchMessageRevoked) => {
184
198
  },
185
199
  ]);
186
200
  const currentMessages = ((_b = (_a = dataCurrent === null || dataCurrent === void 0 ? void 0 : dataCurrent.findResultItems) === null || _a === void 0 ? void 0 : _a.find((item) => item.conversationID === conversationId)) === null || _b === void 0 ? void 0 : _b.messageList) || [];
187
- if (conversationId !== reqConversationID)
201
+ if (isStaleResponse(reqConversationID, reqUseAppHistory))
188
202
  return;
189
203
  // Jumping to a message that has since been revoked: there is nothing to
190
204
  // land on, so don't build the search window around it at all. Tell the
@@ -313,7 +327,7 @@ onSearchMessageRevoked) => {
313
327
  messageList: [],
314
328
  }));
315
329
  };
316
- }, [conversationId, searchClientMsgID, canLoad, userID]);
330
+ }, [conversationId, searchClientMsgID, canLoad, userID, useAppHistory]);
317
331
  return {
318
332
  loadState,
319
333
  latestLoadState,
@@ -3,6 +3,17 @@ import { ChatCategory, PeerType, type ConversationExtra } from "../types/convers
3
3
  export declare const parseConversationExtra: (ex: string | undefined) => ConversationExtra;
4
4
  export declare const resolveConversationChatCategory: (item: ConversationItem) => ChatCategory;
5
5
  export declare const resolveConversationBotId: (item: ConversationItem) => string;
6
+ type SupportSessionSource = {
7
+ conversationID?: string;
8
+ conversationType?: number;
9
+ ex?: string;
10
+ } | null;
11
+ export declare const resolveSupportSessionOwnerId: (item?: SupportSessionSource) => string;
12
+ /**
13
+ * True when the viewer handles a support session someone else owns (e.g. a
14
+ * PDP CS agent). The owner — BIZ/Mall/Obefe customer — keeps the bot view.
15
+ */
16
+ export declare const isSupportSessionAgentView: (item: SupportSessionSource | undefined, viewerUserID?: string) => boolean;
6
17
  export declare const isBotPeer: (item: ConversationItem) => boolean;
7
18
  export declare const isCustomerPeer: (item: ConversationItem) => boolean;
8
19
  export declare const isGroupChat: (item: ConversationItem) => boolean;
@@ -32,4 +43,5 @@ export declare const getGroupIdFromConversationId: (conversationId?: string) =>
32
43
  * matches selfUserId — guessing a peer there would open the wrong thread.
33
44
  */
34
45
  export declare const getPeerUserIdFromConversationId: (conversationId?: string, selfUserId?: string) => string;
46
+ export {};
35
47
  //# sourceMappingURL=conversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/utils/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAW9B,eAAO,MAAM,sBAAsB,GACjC,IAAI,MAAM,GAAG,SAAS,KACrB,iBAcF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,MAAM,gBAAgB,KACrB,YAA4D,CAAC;AAEhE,eAAO,MAAM,wBAAwB,GAAI,MAAM,gBAAgB,KAAG,MAOjE,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEjC,eAAO,MAAM,cAAc,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEtC,eAAO,MAAM,WAAW,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEnC,eAAO,MAAM,8BAA8B,GACzC,MAAM,gBAAgB,EACtB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAKF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,MAAM,gBAAgB,KACrB,MAAM,GAAG,IAGX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,QAAQ,EAClB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAIF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,UAAU,QAAQ,8EAIvD,CAAC;AAKF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,iBAAiB,MAAM,WAG5D,CAAC;AAET;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAC1C,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,MAYF,CAAC"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/utils/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAe,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAW9B,eAAO,MAAM,sBAAsB,GACjC,IAAI,MAAM,GAAG,SAAS,KACrB,iBAcF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,MAAM,gBAAgB,KACrB,YAA4D,CAAC;AAEhE,eAAO,MAAM,wBAAwB,GAAI,MAAM,gBAAgB,KAAG,MAOjE,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,GAAG,IAAI,CAAC;AAIT,eAAO,MAAM,4BAA4B,GACvC,OAAO,oBAAoB,KAC1B,MAiBF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,oBAAoB,GAAG,SAAS,EACtC,eAAe,MAAM,KACpB,OAGF,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEjC,eAAO,MAAM,cAAc,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEtC,eAAO,MAAM,WAAW,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEnC,eAAO,MAAM,8BAA8B,GACzC,MAAM,gBAAgB,EACtB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAKF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,MAAM,gBAAgB,KACrB,MAAM,GAAG,IAGX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,QAAQ,EAClB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAIF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,UAAU,QAAQ,8EAIvD,CAAC;AAKF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,iBAAiB,MAAM,WAG5D,CAAC;AAET;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAC1C,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,MAYF,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { SessionType } from "@droppii-org/wasm-client-sdk";
1
2
  import { ChatCategory, PeerType, } from "../types/conversation";
2
3
  import { DChatApplicationType, } from "../types/chat";
3
4
  const DEFAULT_CONVERSATION_EXTRA = {
@@ -30,6 +31,37 @@ export const resolveConversationBotId = (item) => {
30
31
  return "";
31
32
  }
32
33
  };
34
+ const BOT_SESSION_CONVERSATION_PREFIX = "sg_bot_";
35
+ export const resolveSupportSessionOwnerId = (item) => {
36
+ var _a, _b, _c, _d;
37
+ if (!item)
38
+ return "";
39
+ if (item.conversationType === SessionType.Group) {
40
+ try {
41
+ const parsed = JSON.parse(item.ex || "{}");
42
+ const ownerId = (_c = (_b = (_a = parsed.sessionInfo) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.ownerId) === null || _c === void 0 ? void 0 : _c.trim();
43
+ if (ownerId)
44
+ return ownerId;
45
+ }
46
+ catch (_e) {
47
+ // fall through to the id
48
+ }
49
+ }
50
+ // Core leaves ex empty on some rows; "sg_bot_<botId>_<ownerId>" always
51
+ // carries the owner, so the verdict can't flip between sources.
52
+ const conversationID = (_d = item.conversationID) !== null && _d !== void 0 ? _d : "";
53
+ return conversationID.startsWith(BOT_SESSION_CONVERSATION_PREFIX)
54
+ ? conversationID.slice(conversationID.lastIndexOf("_") + 1)
55
+ : "";
56
+ };
57
+ /**
58
+ * True when the viewer handles a support session someone else owns (e.g. a
59
+ * PDP CS agent). The owner — BIZ/Mall/Obefe customer — keeps the bot view.
60
+ */
61
+ export const isSupportSessionAgentView = (item, viewerUserID) => {
62
+ const ownerId = resolveSupportSessionOwnerId(item);
63
+ return !!ownerId && !!viewerUserID && ownerId !== viewerUserID;
64
+ };
33
65
  export const isBotPeer = (item) => item.peerType === PeerType.BOT;
34
66
  export const isCustomerPeer = (item) => item.peerType === PeerType.CUSTOMER;
35
67
  export const isGroupChat = (item) => item.peerType === PeerType.GROUP;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.6.41",
3
+ "version": "0.6.42",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",