@droppii-org/chat-sdk 0.7.3 → 0.7.4

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.
Files changed (177) hide show
  1. package/dist/components/boot/BootProgressBar.d.ts +13 -0
  2. package/dist/components/boot/BootProgressBar.d.ts.map +1 -0
  3. package/dist/components/boot/BootProgressBar.js +18 -0
  4. package/dist/components/canned-response/CannedResponsePanel.d.ts.map +1 -1
  5. package/dist/components/canned-response/CannedResponsePanel.js +2 -2
  6. package/dist/components/common/BottomSheet.d.ts +18 -0
  7. package/dist/components/common/BottomSheet.d.ts.map +1 -0
  8. package/dist/components/common/BottomSheet.js +49 -0
  9. package/dist/components/common/DeskPanelDrawer.d.ts.map +1 -1
  10. package/dist/components/common/DeskPanelDrawer.js +29 -15
  11. package/dist/components/common/DeskSidePanel.d.ts.map +1 -1
  12. package/dist/components/common/DeskSidePanel.js +1 -1
  13. package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
  14. package/dist/components/compose/ComposeNewMessageModal.js +3 -3
  15. package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
  16. package/dist/components/conversation/ConversationByGroupItem.js +3 -8
  17. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  18. package/dist/components/conversation/ConversationBySessionItem.js +3 -8
  19. package/dist/components/conversation/DeskConversationList.d.ts +4 -1
  20. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  21. package/dist/components/conversation/DeskConversationList.js +2 -2
  22. package/dist/components/conversation/DeskListHeader.d.ts +2 -0
  23. package/dist/components/conversation/DeskListHeader.d.ts.map +1 -1
  24. package/dist/components/conversation/DeskListHeader.js +3 -3
  25. package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
  26. package/dist/components/conversation-detail/ConversationMessageList.js +1 -1
  27. package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
  28. package/dist/components/conversation-inbox/ConversationInboxList.js +12 -3
  29. package/dist/components/conversation-inbox/InboxBootStatus.d.ts +10 -0
  30. package/dist/components/conversation-inbox/InboxBootStatus.d.ts.map +1 -0
  31. package/dist/components/conversation-inbox/InboxBootStatus.js +34 -0
  32. package/dist/components/conversation-inbox/InboxListSkeleton.d.ts +6 -0
  33. package/dist/components/conversation-inbox/InboxListSkeleton.d.ts.map +1 -0
  34. package/dist/components/conversation-inbox/InboxListSkeleton.js +8 -0
  35. package/dist/components/group/GroupPanelHeader.js +1 -1
  36. package/dist/components/icon/strokeIconNodes.d.ts +6 -0
  37. package/dist/components/icon/strokeIconNodes.d.ts.map +1 -1
  38. package/dist/components/icon/strokeIconNodes.js +5 -0
  39. package/dist/components/message/MessageHeader.d.ts +3 -1
  40. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  41. package/dist/components/message/MessageHeader.js +10 -6
  42. package/dist/components/message/MessageList.d.ts +2 -0
  43. package/dist/components/message/MessageList.d.ts.map +1 -1
  44. package/dist/components/message/MessageList.js +15 -4
  45. package/dist/components/message/SelectSession.js +1 -1
  46. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  47. package/dist/components/message/footer/ActionBar.js +2 -2
  48. package/dist/components/message/footer/ComposerTabs.js +1 -1
  49. package/dist/components/message/footer/CrmComposerCard.js +1 -1
  50. package/dist/components/message/footer/EmojiPicker.d.ts +3 -1
  51. package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
  52. package/dist/components/message/footer/EmojiPicker.js +3 -2
  53. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  54. package/dist/components/message/footer/EnterHandler.js +16 -2
  55. package/dist/components/message/footer/MediaActions.d.ts +2 -1
  56. package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
  57. package/dist/components/message/footer/MediaActions.js +4 -4
  58. package/dist/components/message/footer/ToolbarPlugin.js +1 -1
  59. package/dist/components/message/footer/mention/MentionMenu.js +1 -1
  60. package/dist/components/message/headerIconButton.js +1 -1
  61. package/dist/components/message/item/MessageMoreButton.d.ts.map +1 -1
  62. package/dist/components/message/item/MessageMoreButton.js +18 -5
  63. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
  64. package/dist/components/message/item/QuoteMessage.js +3 -50
  65. package/dist/components/message/reply/ReplyPreviewBar.d.ts.map +1 -1
  66. package/dist/components/message/reply/ReplyPreviewBar.js +4 -13
  67. package/dist/components/rich-text-editor/RichTextEditor.d.ts +3 -1
  68. package/dist/components/rich-text-editor/RichTextEditor.d.ts.map +1 -1
  69. package/dist/components/rich-text-editor/RichTextEditor.js +4 -2
  70. package/dist/components/search-conversation/item/SearchItemAsGroup.d.ts.map +1 -1
  71. package/dist/components/search-conversation/item/SearchItemAsGroup.js +3 -3
  72. package/dist/components/search-conversation/item/SearchItemAsMessage.d.ts.map +1 -1
  73. package/dist/components/search-conversation/item/SearchItemAsMessage.js +10 -0
  74. package/dist/components/search-conversation/item/SearchItemAsUser.d.ts.map +1 -1
  75. package/dist/components/search-conversation/item/SearchItemAsUser.js +6 -0
  76. package/dist/components/session/DeskAssignedSession.js +2 -2
  77. package/dist/components/session/FolderRow.js +1 -1
  78. package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -1
  79. package/dist/components/thread/ManualAssignPopover.js +13 -6
  80. package/dist/components/thread/SessionDetailCard.js +1 -1
  81. package/dist/components/thread/SessionTagPicker.js +1 -1
  82. package/dist/components/thread/SupportSessionPanel.d.ts +1 -1
  83. package/dist/components/thread/SupportSessionPanel.d.ts.map +1 -1
  84. package/dist/components/thread/SupportSessionPanel.js +1 -1
  85. package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
  86. package/dist/components/thread/ThreadInfo.js +3 -1
  87. package/dist/components/thread/ThreadInfoHeader.d.ts +1 -1
  88. package/dist/components/thread/ThreadInfoHeader.d.ts.map +1 -1
  89. package/dist/components/thread/ThreadInfoHeader.js +1 -1
  90. package/dist/components/thread/UserSection.js +2 -2
  91. package/dist/constants/layout.d.ts +10 -1
  92. package/dist/constants/layout.d.ts.map +1 -1
  93. package/dist/constants/layout.js +10 -1
  94. package/dist/hooks/common/useDeskShortcuts.d.ts.map +1 -1
  95. package/dist/hooks/common/useDeskShortcuts.js +3 -1
  96. package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts +3 -0
  97. package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts.map +1 -0
  98. package/dist/hooks/common/useIsDeskWorkspaceMounted.js +4 -0
  99. package/dist/hooks/common/useViewportTier.d.ts +4 -0
  100. package/dist/hooks/common/useViewportTier.d.ts.map +1 -0
  101. package/dist/hooks/common/useViewportTier.js +9 -0
  102. package/dist/hooks/common/useVisualViewportHeight.d.ts +10 -0
  103. package/dist/hooks/common/useVisualViewportHeight.d.ts.map +1 -0
  104. package/dist/hooks/common/useVisualViewportHeight.js +30 -0
  105. package/dist/hooks/conversation/useConversationList.d.ts.map +1 -1
  106. package/dist/hooks/conversation/useConversationList.js +31 -0
  107. package/dist/hooks/conversation/useDeskOfflineLock.d.ts +9 -0
  108. package/dist/hooks/conversation/useDeskOfflineLock.d.ts.map +1 -0
  109. package/dist/hooks/conversation/useDeskOfflineLock.js +46 -0
  110. package/dist/hooks/conversation/useDeskThreadNavigation.d.ts +19 -0
  111. package/dist/hooks/conversation/useDeskThreadNavigation.d.ts.map +1 -0
  112. package/dist/hooks/conversation/useDeskThreadNavigation.js +68 -0
  113. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  114. package/dist/hooks/global/useGlobalEvent.js +22 -2
  115. package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
  116. package/dist/hooks/group/useCreateGroup.js +4 -4
  117. package/dist/hooks/init/useChatBootPhase.d.ts +30 -0
  118. package/dist/hooks/init/useChatBootPhase.d.ts.map +1 -0
  119. package/dist/hooks/init/useChatBootPhase.js +72 -0
  120. package/dist/hooks/message/usePinMessage.d.ts +0 -2
  121. package/dist/hooks/message/usePinMessage.d.ts.map +1 -1
  122. package/dist/hooks/message/usePinMessage.js +7 -11
  123. package/dist/hooks/message/useQuotedSenderName.d.ts +14 -0
  124. package/dist/hooks/message/useQuotedSenderName.d.ts.map +1 -0
  125. package/dist/hooks/message/useQuotedSenderName.js +41 -0
  126. package/dist/hooks/user/useAuth.d.ts.map +1 -1
  127. package/dist/hooks/user/useAuth.js +2 -0
  128. package/dist/index.d.ts +5 -1
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +5 -1
  131. package/dist/locales/i18n.d.ts.map +1 -1
  132. package/dist/locales/i18n.js +3 -2
  133. package/dist/locales/i18n.ts +3 -2
  134. package/dist/locales/vi/boot.json +28 -0
  135. package/dist/locales/vi/common.json +1 -1
  136. package/dist/screens/conversation-detail/index.d.ts.map +1 -1
  137. package/dist/screens/conversation-detail/index.js +8 -3
  138. package/dist/screens/desk-message/DeskBootCard.d.ts +7 -0
  139. package/dist/screens/desk-message/DeskBootCard.d.ts.map +1 -0
  140. package/dist/screens/desk-message/DeskBootCard.js +69 -0
  141. package/dist/screens/desk-message/DeskOfflineOverlay.d.ts +10 -0
  142. package/dist/screens/desk-message/DeskOfflineOverlay.d.ts.map +1 -0
  143. package/dist/screens/desk-message/DeskOfflineOverlay.js +11 -0
  144. package/dist/screens/desk-message/DeskWorkspace.d.ts +7 -1
  145. package/dist/screens/desk-message/DeskWorkspace.d.ts.map +1 -1
  146. package/dist/screens/desk-message/DeskWorkspace.js +82 -13
  147. package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts +10 -0
  148. package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts.map +1 -0
  149. package/dist/screens/desk-message/DeskWorkspaceSkeleton.js +18 -0
  150. package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts +8 -0
  151. package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts.map +1 -0
  152. package/dist/screens/desk-message/PhoneThreadFallbackHeader.js +11 -0
  153. package/dist/screens/desk-message/index.d.ts +2 -0
  154. package/dist/screens/desk-message/index.d.ts.map +1 -1
  155. package/dist/screens/desk-message/index.js +47 -3
  156. package/dist/store/boot.d.ts +29 -0
  157. package/dist/store/boot.d.ts.map +1 -0
  158. package/dist/store/boot.js +21 -0
  159. package/dist/store/conversation.d.ts +2 -0
  160. package/dist/store/conversation.d.ts.map +1 -1
  161. package/dist/store/conversation.js +13 -12
  162. package/dist/store/deskPanelHost.d.ts +2 -0
  163. package/dist/store/deskPanelHost.d.ts.map +1 -1
  164. package/dist/store/deskPanelHost.js +1 -0
  165. package/dist/store/type.d.ts +4 -1
  166. package/dist/store/type.d.ts.map +1 -1
  167. package/dist/store/workspaceLayout.d.ts +3 -0
  168. package/dist/store/workspaceLayout.d.ts.map +1 -1
  169. package/dist/store/workspaceLayout.js +2 -0
  170. package/dist/styles/global.css +1 -1
  171. package/dist/utils/conversation.d.ts +1 -0
  172. package/dist/utils/conversation.d.ts.map +1 -1
  173. package/dist/utils/conversation.js +1 -1
  174. package/package.json +1 -1
  175. package/dist/screens/desk-message/ViewportNotice.d.ts +0 -4
  176. package/dist/screens/desk-message/ViewportNotice.d.ts.map +0 -1
  177. package/dist/screens/desk-message/ViewportNotice.js +0 -9
@@ -209,6 +209,12 @@ export declare const STROKE_ICON_NODES: {
209
209
  x2: string;
210
210
  y2: string;
211
211
  }])[];
212
+ menu: [string, {
213
+ x1: string;
214
+ y1: string;
215
+ x2: string;
216
+ y2: string;
217
+ }][];
212
218
  "message-circle": [string, {
213
219
  d: string;
214
220
  }][];
@@ -1 +1 @@
1
- {"version":3,"file":"strokeIconNodes.d.ts","sourceRoot":"","sources":["../../../src/components/icon/strokeIconNodes.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE1E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Qc,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,iBAAiB,CAAC"}
1
+ {"version":3,"file":"strokeIconNodes.d.ts","sourceRoot":"","sources":["../../../src/components/icon/strokeIconNodes.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE1E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiRc,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,iBAAiB,CAAC"}
@@ -135,6 +135,11 @@ export const STROKE_ICON_NODES = {
135
135
  ["polyline", { points: "16 17 21 12 16 7" }],
136
136
  ["line", { x1: "21", y1: "12", x2: "9", y2: "12" }],
137
137
  ],
138
+ "menu": [
139
+ ["line", { x1: "3", y1: "12", x2: "21", y2: "12" }],
140
+ ["line", { x1: "3", y1: "6", x2: "21", y2: "6" }],
141
+ ["line", { x1: "3", y1: "18", x2: "21", y2: "18" }],
142
+ ],
138
143
  "message-circle": [
139
144
  ["path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" }],
140
145
  ],
@@ -5,6 +5,8 @@ interface MessageHeaderProps {
5
5
  currentSession?: ISessionResponse;
6
6
  isJoined?: boolean | null;
7
7
  onOpenThreadInfo?: () => void;
8
+ /** Phone desk: back button before the avatar. */
9
+ onBack?: () => void;
8
10
  }
9
11
  type SelectSessionValueType = SessionStatus | SessionTag;
10
12
  export interface SelectSessionOption {
@@ -14,6 +16,6 @@ export interface SelectSessionOption {
14
16
  tintColorClassnameBg: string;
15
17
  bgTintColorClassname: string;
16
18
  }
17
- declare const MessageHeader: ({ onClose, currentSession, isJoined, onOpenThreadInfo, }: MessageHeaderProps) => import("react/jsx-runtime").JSX.Element;
19
+ declare const MessageHeader: ({ onClose, currentSession, isJoined, onOpenThreadInfo, onBack, }: MessageHeaderProps) => import("react/jsx-runtime").JSX.Element;
18
20
  export default MessageHeader;
19
21
  //# sourceMappingURL=MessageHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AASzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgB/C,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,KAAK,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,QAAA,MAAM,aAAa,GAAI,0DAKpB,kBAAkB,4CAuZpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AASzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgB/C,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,KAAK,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,QAAA,MAAM,aAAa,GAAI,kEAMpB,kBAAkB,4CAmbpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Avatar, Button, message } from "antd";
4
4
  import { Icon } from "../../components/icon";
5
+ import { StrokeIcon } from "../../components/icon/StrokeIcon";
5
6
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
6
7
  import useConversationStore from "../../store/conversation";
7
8
  import MediaCollection from "../../components/media-collection";
@@ -27,7 +28,7 @@ import { PeerType } from "../../types/conversation";
27
28
  import { shouldShowHeaderMemberCount } from "../../utils/headerMemberCount";
28
29
  import TintedAvatar from "../../components/common/TintedAvatar";
29
30
  import { splitDisplayName } from "../../components/conversation/rowDisplayName";
30
- const MessageHeader = ({ onClose, currentSession, isJoined, onOpenThreadInfo, }) => {
31
+ const MessageHeader = ({ onClose, currentSession, isJoined, onOpenThreadInfo, onBack, }) => {
31
32
  var _a, _b, _c, _d;
32
33
  const { t } = useTranslation();
33
34
  const isCx = useAuthStore((state) => state.isCx);
@@ -202,11 +203,14 @@ const MessageHeader = ({ onClose, currentSession, isJoined, onOpenThreadInfo, })
202
203
  setCurrentSessionStatus(currentSession.status);
203
204
  }
204
205
  }, [currentSession]);
206
+ const avatarNode = (_jsx(_Fragment, { children: showOwnerPlaceholder ? (_jsx("span", { "aria-hidden": true, className: "h-10 w-10 shrink-0 animate-pulse rounded-full bg-gray-25" })) : ownerIdentity ? (isCrm ? (_jsx(TintedAvatar, { name: headerShortName, src: ownerIdentity.avatarUrl, size: 40 })) : (_jsx(Avatar, { src: ownerIdentity.avatarUrl, size: "large", alt: "", className: "min-w-10 min-h-10", children: ((_c = headerName === null || headerName === void 0 ? void 0 : headerName.charAt) === null || _c === void 0 ? void 0 : _c.call(headerName, 0)) || "A" }))) : shouldShowMemberCount ? (_jsx(GroupCompositeAvatar, { groupID: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID, faceURL: avatar, ownerUserID: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.ownerUserID, groupName: displayName, size: 40, className: "min-w-10 min-h-10" })) : isCrm ? (_jsx(TintedAvatar, { name: headerShortName, src: avatar, size: 40 })) : (_jsx(Avatar, { src: avatar, size: "large", alt: "", className: "min-w-10 min-h-10", children: ((_d = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _d === void 0 ? void 0 : _d.call(displayName, 0)) || "A" })) }));
205
207
  return (_jsxs("div", { className: `px-4 py-3 flex items-center border-b gap-3 bg-white ${isCrm
206
- ? "flex-nowrap border-gray-25 [container-type:inline-size]"
207
- : "flex-wrap"}`, "data-cp-name": "MessageHeader", children: [showOwnerPlaceholder ? (_jsx("span", { "aria-hidden": true, className: "h-10 w-10 shrink-0 animate-pulse rounded-full bg-gray-25" })) : ownerIdentity ? (isCrm ? (_jsx(TintedAvatar, { name: headerShortName, src: ownerIdentity.avatarUrl, size: 40 })) : (_jsx(Avatar, { src: ownerIdentity.avatarUrl, size: "large", alt: "", className: "min-w-10 min-h-10", children: ((_c = headerName === null || headerName === void 0 ? void 0 : headerName.charAt) === null || _c === void 0 ? void 0 : _c.call(headerName, 0)) || "A" }))) : shouldShowMemberCount ? (_jsx(GroupCompositeAvatar, { groupID: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID, faceURL: avatar, ownerUserID: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.ownerUserID, groupName: displayName, size: 40, className: "min-w-10 min-h-10" })) : isCrm ? (_jsx(TintedAvatar, { name: headerShortName, src: avatar, size: 40 })) : (_jsx(Avatar, { src: avatar, size: "large", alt: "", className: "min-w-10 min-h-10", children: ((_d = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _d === void 0 ? void 0 : _d.call(displayName, 0)) || "A" })), _jsxs("div", { className: `flex flex-col overflow-hidden flex-1 ${isCrm ? "min-w-[96px]" : ""}`, children: [showOwnerPlaceholder ? (_jsxs("span", { "aria-hidden": true, className: "flex flex-col gap-1.5 py-0.5", children: [_jsx("span", { className: "h-4 w-36 animate-pulse rounded bg-gray-25" }), _jsx("span", { className: "h-3 w-24 animate-pulse rounded bg-gray-25" })] })) : (_jsx("p", { className: `text-base truncate ${isCrm ? "m-0 font-semibold leading-[normal] text-gray-900" : ""}`, title: headerName || undefined, children: (isCrm ? headerShortName : headerName) || "" })), showMemberCountLine && (_jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count: memberCount }) })), isCrm && !showOwnerPlaceholder && metaParts.length > 0 && (_jsx("p", { className: "m-0 flex min-w-0 items-center gap-1 overflow-hidden whitespace-nowrap text-xs text-gray-600 [container-type:inline-size]", "data-testid": "header-meta-line", children: metaParts.map((part, index) => (_jsxs(Fragment, { children: [index > 0 && (_jsx("span", { "aria-hidden": true, className: "h-[3px] w-[3px] shrink-0 rounded-full bg-gray-200" })), part] }, index))) }))] }), _jsxs("div", { className: `flex items-center justify-end ${isCrm
208
- ? "shrink-0 gap-3 [@container(max-width:699px)]:gap-2"
209
- : "gap-2 overflow-hidden"}`, children: [_jsxs("div", { className: isCrm ? "flex items-center gap-2" : "contents", children: [isCx && !isRealGroup && (_jsx(SelectSession, { placeholder: t(isCrm ? "reply_status_placeholder" : "select_tag"), options: tagOptions, value: currentSessionTag, onChange: (value) => handleUpdateSession(value, "tag", value === currentSessionTag), excludeOptions: [SessionTag.SLOW_PROCESSING, SessionTag.NONE] })), isCx && !isRealGroup && (_jsx(SelectSession, { placeholder: t("select_status"), options: statusOptions, value: isEnding ? SessionStatus.COMPLETED : currentSessionStatus, disabled: isEnding, onChange: (value) => handleUpdateSession(value, "status", value === currentSessionStatus) }))] }), _jsxs("div", { className: isCrm
208
+ ? "flex-nowrap border-gray-25 [container-type:inline-size] max-lg:flex-wrap phone:gap-2 phone:px-3"
209
+ : "flex-wrap"}`, "data-cp-name": "MessageHeader", children: [onBack && (_jsx(Button, { type: "text", shape: "default", "data-desk-back": true, className: "h-10 w-10 shrink-0 p-0 text-gray-900", onClick: onBack, "aria-label": t("back"), children: _jsx(StrokeIcon, { name: "chevron-left", size: 20 }) })), isCrm ? (_jsx("span", { className: "contents phone:hidden", children: avatarNode })) : (avatarNode), _jsxs("div", { className: `flex flex-col overflow-hidden flex-1 ${isCrm ? "min-w-[96px] max-[359px]:min-w-[72px]" : ""}`, children: [showOwnerPlaceholder ? (_jsxs("span", { "aria-hidden": true, className: "flex flex-col gap-1.5 py-0.5", children: [_jsx("span", { className: "h-4 w-36 animate-pulse rounded bg-gray-25" }), _jsx("span", { className: "h-3 w-24 animate-pulse rounded bg-gray-25" })] })) : (_jsx("p", { className: `text-base truncate ${isCrm ? "m-0 font-semibold leading-[normal] text-gray-900" : ""}`, title: headerName || undefined, children: (isCrm ? headerShortName : headerName) || "" })), showMemberCountLine && (_jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count: memberCount }) })), isCrm && !showOwnerPlaceholder && metaParts.length > 0 && (_jsx("p", { className: "m-0 flex min-w-0 items-center gap-1 overflow-hidden whitespace-nowrap text-xs text-gray-600 [container-type:inline-size]", "data-testid": "header-meta-line", children: metaParts.map((part, index) => (_jsxs(Fragment, { children: [index > 0 && (_jsx("span", { "aria-hidden": true, className: "h-[3px] w-[3px] shrink-0 rounded-full bg-gray-200" })), part] }, index))) }))] }), _jsxs("div", { className: `flex items-center justify-end ${isCrm
210
+ ? "shrink-0 gap-3 [@container(max-width:699px)]:gap-2 max-lg:contents"
211
+ : "gap-2 overflow-hidden"}`, children: [_jsxs("div", { className: isCrm
212
+ ? "flex items-center gap-2 max-lg:order-last max-lg:w-full max-lg:empty:hidden"
213
+ : "contents", children: [isCx && !isRealGroup && (_jsx(SelectSession, { placeholder: t(isCrm ? "reply_status_placeholder" : "select_tag"), options: tagOptions, value: currentSessionTag, onChange: (value) => handleUpdateSession(value, "tag", value === currentSessionTag), excludeOptions: [SessionTag.SLOW_PROCESSING, SessionTag.NONE] })), isCx && !isRealGroup && (_jsx(SelectSession, { placeholder: t("select_status"), options: statusOptions, value: isEnding ? SessionStatus.COMPLETED : currentSessionStatus, disabled: isEnding, onChange: (value) => handleUpdateSession(value, "status", value === currentSessionStatus) }))] }), _jsxs("div", { className: isCrm
210
214
  ? "flex items-center gap-1 [@container(max-width:699px)]:gap-0.5"
211
215
  : "contents", children: [_jsx(SearchDrawer, {}), canViewMemberList && (_jsx(GroupMembersDrawer, { groupID: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID, conversationId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID, currentSession: currentSession })), _jsx(MediaCollection, {}), _jsx(Button, { type: "text", shape: "default", className: headerIconButtonClass(isCrm, isInfoPanelOpen), onClick: handleInfoToggle, "aria-label": t("thread_info_open"), title: t("thread_info_open"), children: _jsx(HeaderIcon, { isCrm: isCrm, stroke: "panel-right", font: "align-justify-o" }) }), !!onClose && (_jsx(Button, { type: "text", shape: "default", className: "text-[var(--dc-color-header-icon)] w-8 h-8 p-0", onClick: onClose, "aria-label": t("close"), children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] })] }), isCrm && currentSession && (_jsx(EndSessionConfirm, { open: isEndConfirmOpen, session: currentSession, customerName: headerName || "", onConfirm: () => {
212
216
  setIsEndConfirmOpen(false);
@@ -11,6 +11,8 @@ interface MessageListProps {
11
11
  onSearchMessageRevoked?: () => void;
12
12
  /** Renders the pinned-message bar under the header (CRM desk only). */
13
13
  showPinnedMessageBar?: boolean;
14
+ /** Phone desk: shows a back button in the header. */
15
+ onBack?: () => void;
14
16
  }
15
17
  declare const MessageList: (props: MessageListProps) => import("react/jsx-runtime").JSX.Element;
16
18
  export default MessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAua3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAub3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMessage } from "../../hooks/message/useMessage";
4
4
  import { Button, Empty, Spin, message as toastMessage } from "antd";
5
+ import clsx from "clsx";
5
6
  import { useCallback, useEffect, useState } from "react";
6
7
  import dayjs from "dayjs";
7
8
  import isToday from "dayjs/plugin/isToday";
@@ -37,7 +38,7 @@ dayjs.extend(isToday);
37
38
  const MessageList = (props) => {
38
39
  const { t } = useTranslation();
39
40
  const { user, connectStatus, allowPinMessageWithoutPermission } = useChatContext();
40
- const { onClose, conversationId, searchClientMsgID, openCreateCannedModal, onOpenThreadInfo, onCopyMessageLink, onSearchMessageRevoked, showPinnedMessageBar = false, } = props;
41
+ const { onClose, conversationId, searchClientMsgID, openCreateCannedModal, onOpenThreadInfo, onCopyMessageLink, onSearchMessageRevoked, showPinnedMessageBar = false, onBack, } = props;
41
42
  const isCrm = useAuthStore((state) => state.isCrm);
42
43
  const conversationData = useConversationStore((state) => state.conversationData);
43
44
  const permissions = useGroupPermission(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID);
@@ -202,6 +203,16 @@ const MessageList = (props) => {
202
203
  emitter.off("UPDATE_SESSION", handler);
203
204
  };
204
205
  }, [conversationId, refetchConversationSessions]);
206
+ // Phone desk: a deep-linked thread shows loading, then the empty state if it never arrives.
207
+ const [loadTimedOut, setLoadTimedOut] = useState(false);
208
+ const isLoadingThread = Boolean(onBack && conversationId && !loadTimedOut);
209
+ useEffect(() => {
210
+ setLoadTimedOut(false);
211
+ if (!onBack || !conversationId || conversationData)
212
+ return;
213
+ const timer = setTimeout(() => setLoadTimedOut(true), 8000);
214
+ return () => clearTimeout(timer);
215
+ }, [onBack, conversationId, conversationData]);
205
216
  useEffect(() => {
206
217
  if (!openMenuId)
207
218
  return;
@@ -213,9 +224,9 @@ const MessageList = (props) => {
213
224
  return () => scrollContainer.removeEventListener("scroll", close);
214
225
  }, [openMenuId]);
215
226
  if (!conversationData) {
216
- return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
227
+ return (_jsx("div", { className: clsx("flex flex-1 items-center justify-center h-full", onBack && "min-h-0"), children: connectStatus !== ConnectStatus.Connected || isLoadingThread ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
217
228
  }
218
- return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), showPinnedMessageBar && (_jsx(PinnedMessageBar, { genericSubtitle: true, canUnpin: canPinMessage })), _jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [!loadState.initLoading && showJumpButton && (_jsx(UnreadMessageJumpButton, { className: "absolute right-0 top-4 z-10", onClick: handleJumpToFirstUnread })), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerProps: isCrm
229
+ return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo, onBack: onBack }), showPinnedMessageBar && (_jsx(PinnedMessageBar, { genericSubtitle: true, canUnpin: canPinMessage })), _jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [!loadState.initLoading && showJumpButton && (_jsx(UnreadMessageJumpButton, { className: "absolute right-0 top-4 z-10", onClick: handleJumpToFirstUnread })), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerProps: isCrm
219
230
  ? {
220
231
  role: "log",
221
232
  "aria-live": "off",
@@ -223,7 +234,7 @@ const MessageList = (props) => {
223
234
  tabIndex: 0,
224
235
  onKeyDown: handleMessageLogKeyDown,
225
236
  }
226
- : undefined, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal, onCopyMessageLink: handleCopyMessageLink, showUnreadDivider: !!unreadDividerMessageId &&
237
+ : undefined, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal, onCopyMessageLink: onCopyMessageLink ? handleCopyMessageLink : undefined, showUnreadDivider: !!unreadDividerMessageId &&
227
238
  message.clientMsgID === unreadDividerMessageId, onUnreadDividerSeen: markDividerSeen }, message.clientMsgID))) })] }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), (Boolean(rawGroupId) && isCheckingMembership) || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : !isConfirmedGroup && shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", "data-cp-name": "JoinGroupRequired", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : isViewOnlyMember ? (
228
239
  // Brief fallback while isJoined catches up after the kick event.
229
240
  _jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t(isRestrictedByGroupMute
@@ -18,7 +18,7 @@ const SelectSession = ({ options, value, onChange, excludeOptions, placeholder,
18
18
  onChange(val);
19
19
  };
20
20
  return (_jsx("div", { className: "relative shrink-0", "data-cp-name": "SelectSession", children: _jsx(Select, { className: isCrm
21
- ? `!h-8 ${crmClassName !== null && crmClassName !== void 0 ? crmClassName : ""} [&_.ant-select-selection-item]:!pe-0 [&_.ant-select-selector]:!h-8 [&_.ant-select-selector]:!pl-[10px] [&_.ant-select-selector]:!pr-[26px] [&_.ant-select-selection-item]:!text-[13px] [&_.ant-select-selection-item]:!font-medium [&_.ant-select-selection-placeholder]:!text-[13px] [&_.ant-select-arrow]:!end-[6px]`
21
+ ? `!h-8 touch:!h-10 touch:[&_.ant-select-selector]:!h-10 touch:[&_.ant-select-selection-item]:!leading-[38px] touch:[&_.ant-select-selection-placeholder]:!leading-[38px] ${crmClassName !== null && crmClassName !== void 0 ? crmClassName : ""} [&_.ant-select-selection-item]:!pe-0 [&_.ant-select-selector]:!h-8 [&_.ant-select-selector]:!pl-[10px] [&_.ant-select-selector]:!pr-[26px] [&_.ant-select-selection-item]:!text-[13px] [&_.ant-select-selection-item]:!font-medium [&_.ant-select-selection-placeholder]:!text-[13px] [&_.ant-select-arrow]:!end-[6px]`
22
22
  : `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.bgTintColorClassname} custom-select min-w-[100px]`, popupClassName: isCrm
23
23
  ? "[&_.ant-select-item-option-selected]:!bg-primary-25 [&_.ant-select-item-option-selected]:!font-normal"
24
24
  : undefined, placement: "bottomLeft", placeholder: placeholder, "aria-label": placeholder, options: selectOptions, value: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value, onSelect: handleSelect, disabled: disabled, popupMatchSelectWidth: false, suffixIcon: isCrm ? (_jsx(StrokeIcon, { name: "chevron-down", size: 16, className: "text-gray-600" })) : (_jsx(Icon, { icon: "angle-down-o", className: `${isCrm ? "text-gray-600" : selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.tintColorClassname} mt-[2px]`, size: 14 })), menuItemSelectedIcon: null, labelRender: () => ["NONE", ""].includes(selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value)
@@ -1 +1 @@
1
- {"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AA4BA,UAAU,cAAc;IAEtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,QAAA,MAAM,SAAS,GAAI,gCAAwC,cAAc,4CA+JxE,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AA4BA,UAAU,cAAc;IAEtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,QAAA,MAAM,SAAS,GAAI,gCAAwC,cAAc,4CAmKxE,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -97,9 +97,9 @@ const ActionBar = ({ formatOpen = false, onToggleFormat }) => {
97
97
  });
98
98
  }, [editor]);
99
99
  if (isCrm) {
100
- return (_jsxs("div", { "data-cp-name": "ActionBar", className: "flex items-center gap-0.5", children: [_jsx(Button, { type: "text", shape: "default", "aria-label": t("composer_format"), title: t("composer_format"), "aria-pressed": formatOpen, onClick: onToggleFormat, className: clsx("!flex !h-8 !w-8 items-center justify-center rounded-lg p-0 hover:!bg-gray-5 hover:!text-gray-900", formatOpen ? "!bg-primary-25 !text-primary-500" : "text-gray-600"), children: _jsx(StrokeIcon, { name: "type", size: 18 }) }), _jsx(MediaActions, { crmComposer: true, listUploadFiles: listUploadFiles, onFilesChange: setListUploadFiles }), _jsx("span", { className: "flex-1" }), _jsxs("button", { type: "button", onClick: handleSend, disabled: !canSend, className: clsx("flex h-8 items-center gap-1.5 whitespace-nowrap rounded-lg border px-3.5 text-sm font-medium text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400", isInternal
100
+ return (_jsxs("div", { "data-cp-name": "ActionBar", className: "flex items-center gap-0.5", children: [_jsx(Button, { type: "text", shape: "default", "aria-label": t("composer_format"), title: t("composer_format"), "aria-pressed": formatOpen, onClick: onToggleFormat, className: clsx("!flex !h-8 !w-8 items-center justify-center rounded-lg p-0 hover:!bg-gray-5 hover:!text-gray-900 touch:min-h-10 touch:min-w-10", formatOpen ? "!bg-primary-25 !text-primary-500" : "text-gray-600"), children: _jsx(StrokeIcon, { name: "type", size: 18 }) }), _jsx(MediaActions, { crmComposer: true, hideEmojiOnPhone: true, listUploadFiles: listUploadFiles, onFilesChange: setListUploadFiles }), _jsx("span", { className: "flex-1" }), _jsxs("button", { type: "button", onClick: handleSend, disabled: !canSend, "aria-label": isInternal ? t("composer_send_note") : t("composer_send"), className: clsx("flex h-8 items-center gap-1.5 whitespace-nowrap rounded-lg border px-3.5 text-sm font-medium text-white touch:min-h-10 max-[359px]:w-10 max-[359px]:justify-center max-[359px]:px-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-400", isInternal
101
101
  ? "border-warning-800 bg-warning-800 hover:bg-warning-900"
102
- : "border-primary-400 bg-primary-400 hover:bg-primary-500", !canSend && "cursor-not-allowed opacity-40"), children: [_jsx(StrokeIcon, { name: "send", size: 18 }), isInternal ? t("composer_send_note") : t("composer_send")] })] }));
102
+ : "border-primary-400 bg-primary-400 hover:bg-primary-500", !canSend && "cursor-not-allowed opacity-40"), children: [_jsx(StrokeIcon, { name: "send", size: 18 }), _jsx("span", { className: "max-[359px]:sr-only", children: isInternal ? t("composer_send_note") : t("composer_send") })] })] }));
103
103
  }
104
104
  return (_jsxs("div", { "data-cp-name": "ActionBar", className: "flex items-center justify-between px-4", children: [_jsx(MediaActions, { listUploadFiles: listUploadFiles, onFilesChange: setListUploadFiles, buttonClassName: "text-gray-500 w-8 h-8 p-0" }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: handleSend, disabled: !canSend, children: _jsx(Icon, { icon: "send-b", size: 28, className: `${canSend ? "text-[var(--dc-color-primary)]" : "text-gray-400"}` }) })] }));
105
105
  };
@@ -16,7 +16,7 @@ const ComposerTabs = ({ canUseInternal, isInternal, lockToInternal, setIsInterna
16
16
  return (_jsx("div", { role: "group", "aria-label": t("composer_mode_label"), "data-cp-name": "ComposerTabs", className: "flex shrink-0 gap-0.5 rounded-lg bg-[var(--dc-color-neutral-surface)] p-1", children: MODES.map(({ isInternal: modeIsInternal, labelKey, icon }) => {
17
17
  const selected = isInternal === modeIsInternal;
18
18
  const disabled = lockToInternal && !modeIsInternal;
19
- return (_jsxs("button", { type: "button", "aria-pressed": selected, disabled: disabled, onClick: () => setIsInternal(modeIsInternal), className: clsx("flex h-7 items-center gap-1.5 whitespace-nowrap rounded px-2.5 text-[13px] focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-400", selected
19
+ return (_jsxs("button", { type: "button", "aria-pressed": selected, disabled: disabled, onClick: () => setIsInternal(modeIsInternal), className: clsx("flex h-7 touch:min-h-10 items-center gap-1.5 whitespace-nowrap rounded px-2.5 text-[13px] focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-400", selected
20
20
  ? clsx("bg-white font-semibold shadow-[0_1px_2px_rgba(57,62,64,0.12)]", modeIsInternal ? "text-warning-800" : "text-gray-900")
21
21
  : "font-medium text-gray-600 hover:text-gray-900", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(StrokeIcon, { name: icon, size: 16 }), t(labelKey)] }, labelKey));
22
22
  }) }));
@@ -21,7 +21,7 @@ const CrmComposerCard = ({ canUseInternal, isInternal, lockToInternal, setIsInte
21
21
  }, [replyLocked]);
22
22
  return (_jsxs("div", { "data-cp-name": "CrmComposerCard", "data-internal": isInternal, className: clsx("flex flex-col gap-1.5 rounded-xl border p-2", isInternal
23
23
  ? "border-warning-400/60 bg-warning-25"
24
- : "border-[color:var(--dc-composer-border)] bg-[var(--dc-composer-bg)] focus-within:border-[color:var(--dc-composer-border-focus)] focus-within:shadow-[0_0_0_3px_var(--dc-color-primary-light)]", replyLocked && "opacity-50"), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 [container-type:inline-size]", children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), isInternal ? (_jsxs("span", { role: "note", title: t("composer_internal_notice"), className: "flex min-w-0 items-center gap-1 whitespace-nowrap text-xs font-medium text-warning-800", children: [_jsx(StrokeIcon, { name: "lock", size: 14 }), _jsx("span", { className: "truncate [@container(max-width:419px)]:sr-only", children: t("composer_internal_notice") })] })) : (_jsxs("span", { className: "ml-auto flex min-w-0 items-center gap-1 overflow-hidden whitespace-nowrap text-xs text-gray-600 [@container(max-width:309px)]:!hidden", children: [_jsx(Kbd, { children: "Enter" }), " ", t("composer_hint_enter"), _jsxs("span", { className: "flex items-center gap-1 [@container(max-width:499px)]:!hidden", children: ["\u00B7 ", _jsx(Kbd, { children: "Shift" }), " + ", _jsx(Kbd, { children: "Enter" }), " ", t("composer_hint_newline")] })] }))] }), _jsxs("div", { ref: fieldRef, className: "contents", children: [_jsx(ComposerEditor, { isInternal: isInternal, placeholderText: isInternal
24
+ : "border-[color:var(--dc-composer-border)] bg-[var(--dc-composer-bg)] focus-within:border-[color:var(--dc-composer-border-focus)] focus-within:shadow-[0_0_0_3px_var(--dc-color-primary-light)]", replyLocked && "opacity-50"), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 [container-type:inline-size]", children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), isInternal ? (_jsxs("span", { role: "note", title: t("composer_internal_notice"), className: "flex min-w-0 items-center gap-1 whitespace-nowrap text-xs font-medium text-warning-800", children: [_jsx(StrokeIcon, { name: "lock", size: 14 }), _jsx("span", { className: "truncate [@container(max-width:419px)]:sr-only", children: t("composer_internal_notice") })] })) : (_jsxs("span", { className: "ml-auto flex min-w-0 items-center gap-1 overflow-hidden whitespace-nowrap text-xs text-gray-600 [@container(max-width:309px)]:!hidden touch:!hidden", children: [_jsx(Kbd, { children: "Enter" }), " ", t("composer_hint_enter"), _jsxs("span", { className: "flex items-center gap-1 [@container(max-width:499px)]:!hidden", children: ["\u00B7 ", _jsx(Kbd, { children: "Shift" }), " + ", _jsx(Kbd, { children: "Enter" }), " ", t("composer_hint_newline")] })] }))] }), _jsxs("div", { ref: fieldRef, className: "contents", children: [_jsx(ComposerEditor, { isInternal: isInternal, placeholderText: isInternal
25
25
  ? t("composer_internal_placeholder")
26
26
  : replyLocked
27
27
  ? t("composer_closed_placeholder")
@@ -1,7 +1,9 @@
1
1
  interface EmojiPickerProps {
2
2
  onEmojiSelect: (emoji: string) => void;
3
3
  onClose: () => void;
4
+ /** Extra classes for the root (CRM clamps the width on tablets). */
5
+ className?: string;
4
6
  }
5
- declare const EmojiPicker: ({ onEmojiSelect, onClose }: EmojiPickerProps) => import("react/jsx-runtime").JSX.Element;
7
+ declare const EmojiPicker: ({ onEmojiSelect, onClose, className }: EmojiPickerProps) => import("react/jsx-runtime").JSX.Element;
6
8
  export default EmojiPicker;
7
9
  //# sourceMappingURL=EmojiPicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EmojiPicker.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,4BAA4B,gBAAgB,4CAiMhE,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EmojiPicker.tsx"],"names":[],"mappings":"AAMA,UAAU,gBAAgB;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,WAAW,GAAI,uCAAuC,gBAAgB,4CAiM3E,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
+ import clsx from "clsx";
4
5
  import { useTranslation } from "react-i18next";
5
- const EmojiPicker = ({ onEmojiSelect, onClose }) => {
6
+ const EmojiPicker = ({ onEmojiSelect, onClose, className }) => {
6
7
  const { t } = useTranslation("common");
7
8
  const [activeCategory, setActiveCategory] = useState("smileys");
8
9
  const emojiCategories = {
@@ -148,7 +149,7 @@ const EmojiPicker = ({ onEmojiSelect, onClose }) => {
148
149
  ],
149
150
  },
150
151
  };
151
- return (_jsx("div", { "data-cp-name": "EmojiPicker", className: "absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-xl shadow-lg w-96 z-50", children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: t("choose_emoji") }), _jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 text-xl", children: "\u00D7" })] }), _jsx("div", { className: "flex gap-1 mb-3", children: Object.entries(emojiCategories).map(([key, category]) => (_jsx("button", { onClick: () => setActiveCategory(key), className: `px-3 py-1 text-xs rounded-full transition-colors ${activeCategory === key
152
+ return (_jsx("div", { "data-cp-name": "EmojiPicker", className: clsx("absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-xl shadow-lg w-96 z-50", className), children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: t("choose_emoji") }), _jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 text-xl", children: "\u00D7" })] }), _jsx("div", { className: "flex gap-1 mb-3", children: Object.entries(emojiCategories).map(([key, category]) => (_jsx("button", { onClick: () => setActiveCategory(key), className: `px-3 py-1 text-xs rounded-full transition-colors ${activeCategory === key
152
153
  ? "bg-blue-100 text-blue-600"
153
154
  : "text-gray-500 hover:text-gray-700"}`, children: category.name }, key))) }), _jsx("div", { className: "grid grid-cols-8 gap-1 max-h-48 overflow-y-auto", children: emojiCategories[activeCategory].emojis.map((emoji, index) => (_jsx("button", { onClick: () => onEmojiSelect(emoji), className: "p-2 text-xl hover:bg-gray-100 rounded-lg transition-colors", children: emoji }, index))) })] }) }));
154
155
  };
@@ -1 +1 @@
1
- {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAyBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAmJnC"}
1
+ {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AA2BA,MAAM,CAAC,OAAO,UAAU,YAAY,SA+JnC"}
@@ -10,10 +10,13 @@ import { useMessageFooterContext } from ".";
10
10
  import useConversationStore from "../../../store/conversation";
11
11
  import emitter, { emit } from "../../../utils/events";
12
12
  import { extractMentions } from "./mention/extractMentions";
13
+ import useAuthStore from "../../../store/auth";
14
+ import { LAYOUT_MEDIA } from "../../../constants/layout";
13
15
  export default function EnterHandler() {
14
16
  const [editor] = useLexicalComposerContext();
15
17
  const { onSendMessage, isInternal, listUploadFiles, isOpenCanned, activeCannedIndex, isMentionMenuOpen, } = useMessageFooterContext();
16
18
  const shiftEnterCount = useRef(0);
19
+ const isCrm = useAuthStore((state) => state.isCrm);
17
20
  const quotedMessage = useConversationStore((state) => state.quotedMessage);
18
21
  useEffect(() => {
19
22
  return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
@@ -23,9 +26,19 @@ export default function EnterHandler() {
23
26
  return false;
24
27
  }
25
28
  // Case 1: Enter (submit, chặn xuống dòng)
26
- if (!event.shiftKey) {
27
- event.preventDefault();
29
+ // Only CRM tolerates a null event; other hosts keep the original strict path.
30
+ const shift = isCrm ? event === null || event === void 0 ? void 0 : event.shiftKey : event.shiftKey;
31
+ if (!shift) {
28
32
  shiftEnterCount.current = 0;
33
+ // CRM touch < 1024: leave the keydown untouched so Lexical (or iOS's native
34
+ // beforeinput) inserts the paragraph; the Send button sends.
35
+ if (isCrm && !isOpenCanned && window.matchMedia(LAYOUT_MEDIA.touch).matches) {
36
+ return false;
37
+ }
38
+ // A null event (IME composition end) has nothing to send or cancel on desktop.
39
+ if (isCrm && !event)
40
+ return true;
41
+ event.preventDefault();
29
42
  if (isOpenCanned) {
30
43
  emit("CANNED_RESPONSE_SELECT", activeCannedIndex);
31
44
  return true;
@@ -108,6 +121,7 @@ export default function EnterHandler() {
108
121
  isOpenCanned,
109
122
  activeCannedIndex,
110
123
  isMentionMenuOpen,
124
+ isCrm,
111
125
  ]);
112
126
  useEffect(() => {
113
127
  const clearEditor = () => {
@@ -5,7 +5,8 @@ interface MediaActionsProps {
5
5
  buttonClassName?: string;
6
6
  showMediaUpload?: boolean;
7
7
  crmComposer?: boolean;
8
+ hideEmojiOnPhone?: boolean;
8
9
  }
9
- export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload, crmComposer, }: MediaActionsProps): import("react/jsx-runtime").JSX.Element;
10
+ export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload, crmComposer, hideEmojiOnPhone, }: MediaActionsProps): import("react/jsx-runtime").JSX.Element;
10
11
  export {};
11
12
  //# sourceMappingURL=MediaActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAuD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,EACtB,WAAmB,GACpB,EAAE,iBAAiB,2CA6LnB"}
1
+ {"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAuD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,EACtB,WAAmB,EACnB,gBAAwB,GACzB,EAAE,iBAAiB,2CA+LnB"}
@@ -18,7 +18,7 @@ import useAuthStore from "../../../store/auth";
18
18
  import { StrokeIcon } from "../../../components/icon/StrokeIcon";
19
19
  const EmojiIcon = (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M7.59997 12.4C7.59997 12.4 8.49997 13.2 9.99997 13.2C11.5 13.2 12.4 12.4 12.4 12.4M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997ZM18 9.99997C18 14.4182 14.4182 18 9.99997 18C5.58169 18 1.99997 14.4182 1.99997 9.99997C1.99997 5.58169 5.58169 1.99997 9.99997 1.99997C14.4182 1.99997 18 5.58169 18 9.99997ZM7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
20
20
  const AttachIcon = (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M15.6 10.0001V11.2001C15.6 14.5137 12.9137 17.2001 9.59998 17.2001C6.28626 17.2001 3.59998 14.5137 3.59998 11.2001V6.79999C3.59998 4.59085 5.39084 2.79999 7.59998 2.79999C9.8091 2.79999 11.6 4.59085 11.6 6.79999V11.2C11.6 12.3045 10.7045 13.2 9.59998 13.2C8.49542 13.2 7.59998 12.3045 7.59998 11.2V7.99999", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
21
- export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload = true, crmComposer = false, }) {
21
+ export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload = true, crmComposer = false, hideEmojiOnPhone = false, }) {
22
22
  const [editor] = useLexicalComposerContext();
23
23
  const { t } = useTranslation();
24
24
  const [showEmojiPicker, setShowEmojiPicker] = useState(false);
@@ -96,14 +96,14 @@ export default function MediaActions({ listUploadFiles, onFilesChange, buttonCla
96
96
  return () => document.removeEventListener("mousedown", handleClickOutside);
97
97
  }, []);
98
98
  const btnClass = crmComposer
99
- ? "!flex !h-8 !w-8 items-center justify-center rounded-lg p-0 text-gray-600 hover:!bg-gray-5 hover:!text-gray-900"
99
+ ? "!flex !h-8 !w-8 items-center justify-center rounded-lg p-0 text-gray-600 hover:!bg-gray-5 hover:!text-gray-900 touch:min-h-10 touch:min-w-10"
100
100
  : buttonClassName !== null && buttonClassName !== void 0 ? buttonClassName : "w-8 h-8 p-0 text-gray-500 hover:text-gray-700 hover:bg-gray-100";
101
101
  const activeCls = crmComposer
102
102
  ? "!bg-primary-25 !text-primary-500"
103
103
  : "bg-blue-100 text-blue-600";
104
104
  const crmLabel = (key) => crmComposer ? { "aria-label": t(key), title: t(key) } : {};
105
- return (_jsxs("div", { "data-cp-name": "MediaActions", className: clsx("flex items-center relative", crmComposer ? "gap-0.5" : "gap-1"), ref: containerRef, children: [isCrm && (_jsx(Button, Object.assign({ type: "text", shape: "default", className: clsx(btnClass, isOpenCanned && activeCls), onClick: onPressCanned }, crmLabel("composer_quick_reply"), { children: crmComposer ? (_jsx(StrokeIcon, { name: "hash", size: 18 })) : (_jsx(CannedResponseIcon, { className: clsx("hover:text-gray-700", isOpenCanned && "text-blue-600") })) }))), _jsx(Button, Object.assign({ type: "text", shape: "default", className: clsx(btnClass, showEmojiPicker &&
105
+ return (_jsxs("div", { "data-cp-name": "MediaActions", className: clsx("flex items-center relative", crmComposer ? "gap-0.5" : "gap-1"), ref: containerRef, children: [isCrm && (_jsx(Button, Object.assign({ type: "text", shape: "default", className: clsx(btnClass, isOpenCanned && activeCls), onClick: onPressCanned }, crmLabel("composer_quick_reply"), { children: crmComposer ? (_jsx(StrokeIcon, { name: "hash", size: 18 })) : (_jsx(CannedResponseIcon, { className: clsx("hover:text-gray-700", isOpenCanned && "text-blue-600") })) }))), _jsx(Button, Object.assign({ type: "text", shape: "default", className: clsx(btnClass, hideEmojiOnPhone && "phone:!hidden", showEmojiPicker &&
106
106
  (crmComposer
107
107
  ? activeCls
108
- : "bg-[var(--dc-color-primary-light)] text-[var(--dc-color-primary)]")), onClick: () => setShowEmojiPicker(!showEmojiPicker) }, crmLabel("composer_emoji"), { children: crmComposer ? _jsx(StrokeIcon, { name: "smile", size: 18 }) : EmojiIcon })), showMediaUpload && (_jsxs(_Fragment, { children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, Object.assign({ type: "text", shape: "default", className: btnClass }, crmLabel("composer_attach_image"), { children: crmComposer ? (_jsx(StrokeIcon, { name: "image", size: 18 })) : (_jsx(Icon, { icon: "image-02-o", size: 22 })) })) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, Object.assign({ type: "text", shape: "default", className: btnClass }, crmLabel("composer_attach_file"), { children: crmComposer ? _jsx(StrokeIcon, { name: "paperclip", size: 18 }) : AttachIcon })) })] })), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }));
108
+ : "bg-[var(--dc-color-primary-light)] text-[var(--dc-color-primary)]")), onClick: () => setShowEmojiPicker(!showEmojiPicker) }, crmLabel("composer_emoji"), { children: crmComposer ? _jsx(StrokeIcon, { name: "smile", size: 18 }) : EmojiIcon })), showMediaUpload && (_jsxs(_Fragment, { children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, Object.assign({ type: "text", shape: "default", className: btnClass }, crmLabel("composer_attach_image"), { children: crmComposer ? (_jsx(StrokeIcon, { name: "image", size: 18 })) : (_jsx(Icon, { icon: "image-02-o", size: 22 })) })) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, Object.assign({ type: "text", shape: "default", className: btnClass }, crmLabel("composer_attach_file"), { children: crmComposer ? _jsx(StrokeIcon, { name: "paperclip", size: 18 }) : AttachIcon })) })] })), showEmojiPicker && (_jsx(EmojiPicker, { className: crmComposer ? "max-lg:w-80" : undefined, onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }));
109
109
  }
@@ -105,7 +105,7 @@ export const ToolbarPlugin = ({ compact = false }) => {
105
105
  editor.dispatchCommand(TOGGLE_LINK_COMMAND, "https://");
106
106
  }, [editor]);
107
107
  const getButtonClasses = (isActive) => {
108
- return `w-8 h-8 p-0 border-sm ${isActive
108
+ return `w-8 h-8 p-0 border-sm ${compact ? "touch:min-h-10 touch:min-w-10" : ""} ${isActive
109
109
  ? "bg-blue-100 text-blue-600"
110
110
  : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`;
111
111
  };
@@ -95,7 +95,7 @@ const MentionMenu = ({ groupID, isBotThread }) => {
95
95
  // overlapped the composer's own top portion. MentionMenuOffset
96
96
  // pushes the extra distance from the caret up to the composer's
97
97
  // actual top edge, so the menu clears the input entirely.
98
- _jsxs(MentionMenuOffset, { "data-cp-name": "MentionMenu", className: "absolute left-0 z-50 max-h-[220px] w-[260px] overflow-y-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", children: [_jsx("div", { className: "px-3 py-1 text-xs font-medium text-gray-400", children: t("mention.menu_title") }), typeaheadOptions.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children: limitReached
98
+ _jsxs(MentionMenuOffset, { "data-cp-name": "MentionMenu", className: "absolute left-0 z-50 max-h-[220px] touch:max-h-[min(220px,calc(var(--dc-desk-height,100dvh)-var(--dc-composer-reserve,280px)))] w-[260px] overflow-y-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", children: [_jsx("div", { className: "px-3 py-1 text-xs font-medium text-gray-400", children: t("mention.menu_title") }), typeaheadOptions.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children: limitReached
99
99
  ? t("mention.limit_reached", { max: MAX_MENTIONED_USERS })
100
100
  : t("mention.no_result") })) : (typeaheadOptions.map((option, index) => (_jsxs("button", { type: "button", className: `flex w-full items-center gap-2 px-3 py-2 text-left text-sm ${index === selectedIndex ? "bg-blue-50" : "hover:bg-gray-50"}`, onMouseEnter: () => setHighlightedIndex(index), onClick: () => selectOptionAndCleanUp(option), children: [option.option.isAtAll ? (_jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gray-100 text-gray-500", children: "@" })) : isCrm ? (_jsx(TintedAvatar, { name: option.option.displayName, src: option.option.faceURL, size: 24 })) : (_jsx(Avatar, { size: 24, src: option.option.faceURL, children: option.option.displayName.charAt(0) })), _jsx("span", { className: "min-w-0 flex-1 truncate", children: option.option.displayName })] }, option.key))))] }), anchorElementRef.current);
101
101
  } }));
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Icon } from "../../components/icon";
3
3
  import { StrokeIcon } from "../../components/icon/StrokeIcon";
4
4
  /** Classes of a header icon button; an open panel gets the Primary-25 tile in CRM. */
5
- export const headerIconButtonClass = (isCrm, isOpen) => `${isCrm ? "w-9 h-9 [@container(max-width:699px)]:!w-8 [@container(max-width:699px)]:!h-8" : "w-8 h-8"} p-0 ${isCrm && isOpen
5
+ export const headerIconButtonClass = (isCrm, isOpen) => `${isCrm ? "w-9 h-9 [@container(max-width:699px)]:!w-8 [@container(max-width:699px)]:!h-8 touch:min-h-10 touch:min-w-10" : "w-8 h-8"} p-0 ${isCrm && isOpen
6
6
  ? "bg-primary-25 text-primary-500"
7
7
  : "text-[var(--dc-color-header-icon)]"}`;
8
8
  /** Mockup line icon in CRM, the icon-font glyph elsewhere. */
@@ -1 +1 @@
1
- {"version":3,"file":"MessageMoreButton.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageMoreButton.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAwBtC,UAAU,sBAAsB;IAC9B,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,oFAAoF;AACpF,QAAA,MAAM,iBAAiB,GAAI,mDAMxB,sBAAsB,4CAyDxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"MessageMoreButton.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageMoreButton.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAsCtC,UAAU,sBAAsB;IAC9B,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,oFAAoF;AACpF,QAAA,MAAM,iBAAiB,GAAI,mDAMxB,sBAAsB,4CAmFxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -1,18 +1,29 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef } from "react";
4
- import { Dropdown } from "antd";
4
+ import { Dropdown, Menu } from "antd";
5
5
  import clsx from "clsx";
6
6
  import { StrokeIcon } from "../../../components/icon/StrokeIcon";
7
7
  import { useTranslation } from "react-i18next";
8
8
  import { useEscapeLayer } from "../../../hooks/common/useEscapeLayer";
9
+ import BottomSheet from "../../../components/common/BottomSheet";
10
+ import { LAYOUT_MEDIA } from "../../../constants/layout";
9
11
  // Compact text menu from the design: 260px, 36px rows, 14px text, 18px icons.
10
12
  const MORE_MENU_CLASSNAME = clsx("[&_.ant-dropdown-menu]:!w-[260px]", "[&_.ant-dropdown-menu]:!rounded-xl", "[&_.ant-dropdown-menu]:!p-1", "[&_.ant-dropdown-menu-item]:!h-9", "[&_.ant-dropdown-menu-item]:!rounded-lg", "[&_.ant-dropdown-menu-item]:!px-3", "[&_.ant-dropdown-menu-item]:!py-0", "[&_.ant-dropdown-menu-item]:flex", "[&_.ant-dropdown-menu-item]:items-center", "[&_[data-cp-name=ToolboxMenuRow]]:gap-2.5", "[&_[data-cp-name=ToolboxMenuRow]_span]:!text-sm", "[&_[data-cp-name=ToolboxMenuRow]_span]:!font-normal", "[&_[data-cp-name=ToolboxMenuRow]_svg]:!h-[18px]", "[&_[data-cp-name=ToolboxMenuRow]_svg]:!w-[18px]");
13
+ // Sheet rows: 48px, borderless, same labeled items as the dropdown.
14
+ const SHEET_MENU_CLASSNAME = clsx("!border-0", "[&_.ant-menu-item]:!h-12", "[&_.ant-menu-item]:!leading-[48px]", "[&_.ant-menu-item]:!rounded-lg", "[&_.ant-menu-item]:!px-3", "[&_.ant-menu-item]:!m-0", "[&_.ant-menu-item]:!w-full", "[&_[data-cp-name=ToolboxMenuRow]]:gap-3");
11
15
  /** CRM only: "…" beside the bubble that opens the same text menu as right-click. */
12
16
  const MessageMoreButton = ({ items, open, onOpenChange, isMine, reachable, }) => {
13
17
  const { t } = useTranslation();
14
18
  const buttonRef = useRef(null);
15
19
  useEscapeLayer(open, () => onOpenChange(false));
20
+ // A plain matchMedia read per render, no per-row subscription.
21
+ const isPhoneTier = typeof window !== "undefined" && window.matchMedia(LAYOUT_MEDIA.phone).matches;
22
+ const asSheet = open && isPhoneTier;
23
+ // Mounted on first sheet use only, so rows that never open one carry no Drawer.
24
+ const sheetUsedRef = useRef(false);
25
+ if (asSheet)
26
+ sheetUsedRef.current = true;
16
27
  // Opened from the keyboard: move into the menu; closing returns focus to the button.
17
28
  const openedByKeyboardRef = useRef(false);
18
29
  useEffect(() => {
@@ -25,7 +36,9 @@ const MessageMoreButton = ({ items, open, onOpenChange, isMine, reachable, }) =>
25
36
  // The popup mounts a frame or two after `open`; retry briefly.
26
37
  let tries = 0;
27
38
  const id = window.setInterval(() => {
28
- const item = document.querySelector(".ant-dropdown:not(.ant-dropdown-hidden) [role=menuitem]");
39
+ const item = document.querySelector(asSheet
40
+ ? ".ant-drawer-open [role=menuitem]"
41
+ : ".ant-dropdown:not(.ant-dropdown-hidden) [role=menuitem]");
29
42
  item === null || item === void 0 ? void 0 : item.focus();
30
43
  if (item || ++tries > 20)
31
44
  window.clearInterval(id);
@@ -36,7 +49,7 @@ const MessageMoreButton = ({ items, open, onOpenChange, isMine, reachable, }) =>
36
49
  openedByKeyboardRef.current = false;
37
50
  (_b = buttonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
38
51
  }
39
- }, [open]);
40
- return (_jsx(Dropdown, { menu: { items }, trigger: ["click"], open: open, onOpenChange: onOpenChange, placement: isMine ? "bottomRight" : "bottomLeft", overlayClassName: MORE_MENU_CLASSNAME, children: _jsx("button", { ref: buttonRef, type: "button", "data-testid": "message-more-button", "aria-label": t("message_actions"), title: t("message_actions"), "aria-haspopup": "menu", "aria-expanded": open, tabIndex: reachable || open ? 0 : -1, className: clsx("absolute top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-lg border-0 bg-gray-5 p-0 text-gray-900 opacity-0 transition-opacity hover:bg-gray-25 focus-visible:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200 group-hover/msg:opacity-100 group-focus-within/msg:opacity-100", open && "opacity-100", isMine ? "right-full mr-1" : "left-full ml-1"), children: _jsx(StrokeIcon, { name: "more", size: 18 }) }) }));
52
+ }, [open, asSheet]);
53
+ return (_jsxs(_Fragment, { children: [_jsx(Dropdown, { menu: { items }, trigger: ["click"], open: open && !asSheet, onOpenChange: onOpenChange, placement: isMine ? "bottomRight" : "bottomLeft", overlayClassName: MORE_MENU_CLASSNAME, children: _jsx("button", { ref: buttonRef, type: "button", "data-testid": "message-more-button", "aria-label": t("message_actions"), title: t("message_actions"), "aria-haspopup": isPhoneTier ? "dialog" : "menu", "aria-expanded": open, tabIndex: reachable || open ? 0 : -1, className: clsx("absolute top-1/2 z-[1] flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-lg border-0 bg-gray-5 p-0 text-gray-900 opacity-0 transition-opacity hover:bg-gray-25 focus-visible:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200 group-hover/msg:opacity-100 group-focus-within/msg:opacity-100 touch-any:opacity-100 touch:min-h-10 touch:min-w-10", open && "opacity-100", isMine ? "right-full mr-1" : "left-full ml-1"), children: _jsx(StrokeIcon, { name: "more", size: 18 }) }) }), sheetUsedRef.current && (_jsx(BottomSheet, { open: asSheet, title: t("message_actions"), onClose: () => onOpenChange(false), children: _jsx(Menu, { items: items, selectable: false, onClick: () => onOpenChange(false), className: SHEET_MENU_CLASSNAME }) }))] }));
41
54
  };
42
55
  export default MessageMoreButton;
@@ -1 +1 @@
1
- {"version":3,"file":"QuoteMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/QuoteMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAS3D,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,gBAAgB,GAAI,mCAGvB,qBAAqB,mDA0EvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"QuoteMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/QuoteMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAK3D,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,gBAAgB,GAAI,mCAGvB,qBAAqB,mDAoBvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,62 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useTranslation } from "react-i18next";
3
- import { useCallback, useMemo } from "react";
4
- import useAuthStore from "../../../store/auth";
5
- import useConversationStore from "../../../store/conversation";
6
3
  import ReplyQuoteContent from "../../../components/message/reply/ReplyQuoteContent";
7
- import { useUserUsername } from "../../../hooks/user/useUserUsername";
8
- import { isSupportSessionAgentView } from "../../../utils/conversation";
4
+ import { useQuotedSenderName } from "../../../hooks/message/useQuotedSenderName";
9
5
  const QuoteMessageItem = ({ message, onPressQuoteMessage, }) => {
10
6
  var _a, _b;
11
- const userID = useAuthStore((state) => state.userID);
12
- const isCrm = useAuthStore((state) => state.isCrm);
13
- const conversationData = useConversationStore((state) => state.conversationData);
14
7
  const { t } = useTranslation();
15
- const isChatSupport = useMemo(() => {
16
- var _a, _b, _c;
17
- try {
18
- return (_c = (_b = (_a = JSON.parse((conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _a === void 0 ? void 0 : _a.sessionInfo) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.botId;
19
- }
20
- catch (_d) {
21
- return false;
22
- }
23
- }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex]);
24
8
  const quoteMessage = ((_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage) || ((_b = message === null || message === void 0 ? void 0 : message.atTextElem) === null || _b === void 0 ? void 0 : _b.quoteMessage);
25
- // Outside CRM, an agent identifies quoted senders by username; the session
26
- // owner keeps seeing the thread's own name.
27
- const isAgentView = !isCrm && isSupportSessionAgentView(conversationData, userID);
28
- const quotedUsername = useUserUsername(isAgentView && (quoteMessage === null || quoteMessage === void 0 ? void 0 : quoteMessage.sendID) !== userID
29
- ? quoteMessage === null || quoteMessage === void 0 ? void 0 : quoteMessage.sendID
30
- : undefined);
31
- const getDisplayName = useCallback(({ name, id }) => {
32
- if (isAgentView) {
33
- if (id === userID)
34
- return t("you");
35
- return quotedUsername || name || "";
36
- }
37
- if (!name)
38
- return "";
39
- if (!isCrm && isChatSupport) {
40
- return conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName;
41
- }
42
- if (id === userID)
43
- return t("you");
44
- return name;
45
- }, [
46
- isAgentView,
47
- quotedUsername,
48
- isCrm,
49
- isChatSupport,
50
- conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName,
51
- userID,
52
- t,
53
- ]);
9
+ const { isMe, name } = useQuotedSenderName(quoteMessage);
54
10
  if (!quoteMessage)
55
11
  return null;
56
- const title = getDisplayName({
57
- name: quoteMessage.senderNickname,
58
- id: quoteMessage.sendID,
59
- });
12
+ const title = isMe ? t("you") : name;
60
13
  return (_jsx("div", { className: "flex cursor-pointer items-center overflow-hidden rounded-r-xl bg-white/70", onClick: () => onPressQuoteMessage === null || onPressQuoteMessage === void 0 ? void 0 : onPressQuoteMessage(quoteMessage.clientMsgID || ""), "data-cp-name": "QuoteMessageItem", children: _jsx(ReplyQuoteContent, { message: quoteMessage, title: title }) }));
61
14
  };
62
15
  export default QuoteMessageItem;
@@ -1 +1 @@
1
- {"version":3,"file":"ReplyPreviewBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/reply/ReplyPreviewBar.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,eAAe,sDAmD3B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ReplyPreviewBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/reply/ReplyPreviewBar.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,sDAuC3B,CAAC;AAEF,eAAe,eAAe,CAAC"}