@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
@@ -0,0 +1,13 @@
1
+ export type BootProgressBarMode = "pending" | "indeterminate" | "progress" | "complete";
2
+ interface BootProgressBarProps {
3
+ mode: BootProgressBarMode;
4
+ /** 0-100, read in "progress" mode. */
5
+ value?: number;
6
+ /** Accessible name, announced only in "progress" mode. */
7
+ label?: string;
8
+ className?: string;
9
+ }
10
+ /** Thin bar for one boot step: not started, running for an unknown time, a known percentage, or done. */
11
+ declare const BootProgressBar: ({ mode, value, label, className }: BootProgressBarProps) => import("react/jsx-runtime").JSX.Element;
12
+ export default BootProgressBar;
13
+ //# sourceMappingURL=BootProgressBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BootProgressBar.d.ts","sourceRoot":"","sources":["../../../src/components/boot/BootProgressBar.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,eAAe,GAAG,UAAU,GAAG,UAAU,CAAC;AAExF,UAAU,oBAAoB;IAC5B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yGAAyG;AACzG,QAAA,MAAM,eAAe,GAAI,mCAAuC,oBAAoB,4CAiCnF,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ /** Thin bar for one boot step: not started, running for an unknown time, a known percentage, or done. */
5
+ const BootProgressBar = ({ mode, value = 0, label, className }) => {
6
+ const scale = mode === "complete" ? 1 : mode === "progress" ? value / 100 : 0;
7
+ const a11yProps = mode === "progress"
8
+ ? {
9
+ role: "progressbar",
10
+ "aria-label": label,
11
+ "aria-valuemin": 0,
12
+ "aria-valuemax": 100,
13
+ "aria-valuenow": Math.round(value),
14
+ }
15
+ : { "aria-hidden": true };
16
+ return (_jsx("div", Object.assign({ "data-cp-name": "BootProgressBar", "data-mode": mode, className: clsx("relative h-1 overflow-hidden rounded-full bg-[var(--dc-color-primary-light)]", className) }, a11yProps, { children: mode === "indeterminate" ? (_jsx("span", { className: "absolute inset-y-0 left-0 w-full rounded-full bg-[var(--dc-color-primary)] opacity-40 motion-safe:w-[35%] motion-safe:animate-[dchat-boot-indeterminate_1.3s_ease-in-out_infinite] motion-safe:opacity-100" })) : (_jsx("span", { className: "absolute inset-0 origin-left rounded-full bg-[var(--dc-color-primary)] transition-transform duration-300 ease-out motion-reduce:transition-none", style: { transform: `scaleX(${scale})` } })) })));
17
+ };
18
+ export default BootProgressBar;
@@ -1 +1 @@
1
- {"version":3,"file":"CannedResponsePanel.d.ts","sourceRoot":"","sources":["../../../src/components/canned-response/CannedResponsePanel.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,mBAAmB,EAA8B,MAAM,cAAc,CAAC;AAQ/E,UAAU,wBAAwB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAaD,QAAA,MAAM,mBAAmB,GAAI,oCAI1B,wBAAwB,4CAiJ1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"CannedResponsePanel.d.ts","sourceRoot":"","sources":["../../../src/components/canned-response/CannedResponsePanel.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,mBAAmB,EAA8B,MAAM,cAAc,CAAC;AAQ/E,UAAU,wBAAwB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAaD,QAAA,MAAM,mBAAmB,GAAI,oCAI1B,wBAAwB,4CAmJ1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -65,8 +65,8 @@ const CannedResponsePanel = ({ query, viewedTeamId, onCreate, }) => {
65
65
  };
66
66
  const noQuery = query.length === 0;
67
67
  const empty = !noQuery && !settling && !isLoading && rows.length === 0;
68
- return (_jsxs("div", { "data-cp-name": "CannedResponsePanel", role: "region", "aria-label": t("composer_quick_reply"), className: "absolute inset-x-4 bottom-full z-20 mb-2 overflow-hidden rounded-xl bg-white shadow-[0_16px_16px_-8px_rgba(27,63,228,0.2),0_0_0_1px_rgba(57,62,64,0.06)]", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-25 px-3 py-2.5", children: [_jsx("span", { className: "shrink-0 text-[13px] font-semibold text-gray-900", children: t("composer_quick_reply") }), _jsx("span", { className: "flex-1" }), _jsx(CannedScopeChips, { scopes: scopes, value: filter, onChange: setFilter })] }), _jsxs("div", { className: "max-h-[260px] min-h-[96px] overflow-y-auto p-1", children: [noQuery && (_jsx("p", { className: "px-3 py-6 text-center text-sm text-gray-600", children: t("canned_search_prompt") })), !noQuery && (settling || isLoading) && (_jsx("div", { className: "flex justify-center py-6", children: _jsx(Spin, { size: "small" }) })), isError && !settling && (_jsx("p", { role: "alert", className: "px-3 py-6 text-center text-sm text-danger-600", children: t("canned_loading_failed") })), empty && !isError && (_jsxs("div", { className: "flex flex-col items-center gap-3 px-3 py-5", children: [_jsx("p", { className: "text-center text-sm text-gray-600", children: t("canned_search_empty", { query }) }), _jsxs("button", { type: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault(), onClick: create, className: "flex h-8 items-center gap-1.5 rounded-lg border border-gray-50 bg-white px-3.5 text-sm font-medium text-gray-900 hover:bg-gray-5", children: [_jsx(StrokeIcon, { name: "plus", size: 16 }), t("canned_create_new")] })] })), !settling && rows.length > 0 && (_jsx("ul", { className: "m-0 flex list-none flex-col gap-0.5 p-0", children: rows.map((row, index) => (_jsx(CannedResponseRow, { ref: (el) => {
68
+ return (_jsxs("div", { "data-cp-name": "CannedResponsePanel", role: "region", "aria-label": t("composer_quick_reply"), className: "absolute inset-x-4 bottom-full z-20 mb-2 overflow-hidden rounded-xl bg-white shadow-[0_16px_16px_-8px_rgba(27,63,228,0.2),0_0_0_1px_rgba(57,62,64,0.06)]", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-25 px-3 py-2.5", children: [_jsx("span", { className: "shrink-0 text-[13px] font-semibold text-gray-900", children: t("composer_quick_reply") }), _jsx("span", { className: "flex-1" }), _jsx(CannedScopeChips, { scopes: scopes, value: filter, onChange: setFilter })] }), _jsxs("div", { className: "max-h-[260px] min-h-[96px] touch:min-h-0 touch:max-h-[min(260px,calc(var(--dc-desk-height,100dvh)-var(--dc-composer-reserve,280px)))] overflow-y-auto p-1", children: [noQuery && (_jsx("p", { className: "px-3 py-6 text-center text-sm text-gray-600", children: t("canned_search_prompt") })), !noQuery && (settling || isLoading) && (_jsx("div", { className: "flex justify-center py-6", children: _jsx(Spin, { size: "small" }) })), isError && !settling && (_jsx("p", { role: "alert", className: "px-3 py-6 text-center text-sm text-danger-600", children: t("canned_loading_failed") })), empty && !isError && (_jsxs("div", { className: "flex flex-col items-center gap-3 px-3 py-5", children: [_jsx("p", { className: "text-center text-sm text-gray-600", children: t("canned_search_empty", { query }) }), _jsxs("button", { type: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault(), onClick: create, className: "flex h-8 items-center gap-1.5 rounded-lg border border-gray-50 bg-white px-3.5 text-sm font-medium text-gray-900 hover:bg-gray-5", children: [_jsx(StrokeIcon, { name: "plus", size: 16 }), t("canned_create_new")] })] })), !settling && rows.length > 0 && (_jsx("ul", { className: "m-0 flex list-none flex-col gap-0.5 p-0", children: rows.map((row, index) => (_jsx(CannedResponseRow, { ref: (el) => {
69
69
  itemRefs.current[index] = el;
70
- }, row: row, preview: toPreview(row.content || ""), active: index === activeCannedIndex, onSelect: () => insertRow(row) }, row.id))) }))] }), _jsxs("div", { className: "flex items-center gap-2 border-t border-gray-25 px-3 py-2 text-xs text-gray-600", children: [_jsx(Kbd, { children: "\u2191" }), _jsx(Kbd, { children: "\u2193" }), " ", t("canned_hint_select"), " \u00B7 ", _jsx(Kbd, { children: "Enter" }), " ", t("canned_hint_insert"), " \u00B7", " ", _jsx(Kbd, { children: "Esc" }), " ", t("canned_hint_close"), _jsx("span", { className: "flex-1" }), _jsxs("button", { type: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault(), onClick: create, className: "flex h-7 items-center gap-1.5 rounded-lg px-2 text-[13px] font-medium text-primary-500 hover:bg-primary-25", children: [_jsx(StrokeIcon, { name: "plus", size: 16 }), t("canned_create_quick")] })] })] }));
70
+ }, row: row, preview: toPreview(row.content || ""), active: index === activeCannedIndex, onSelect: () => insertRow(row) }, row.id))) }))] }), _jsxs("div", { className: "flex items-center gap-2 border-t border-gray-25 px-3 py-2 text-xs text-gray-600", children: [_jsxs("span", { className: "contents touch:hidden", children: [_jsx(Kbd, { children: "\u2191" }), _jsx(Kbd, { children: "\u2193" }), " ", t("canned_hint_select"), " \u00B7 ", _jsx(Kbd, { children: "Enter" }), " ", t("canned_hint_insert"), " \u00B7", " ", _jsx(Kbd, { children: "Esc" }), " ", t("canned_hint_close")] }), _jsx("span", { className: "flex-1" }), _jsxs("button", { type: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault(), onClick: create, className: "flex h-7 items-center gap-1.5 rounded-lg px-2 text-[13px] font-medium text-primary-500 hover:bg-primary-25", children: [_jsx(StrokeIcon, { name: "plus", size: 16 }), t("canned_create_quick")] })] })] }));
71
71
  };
72
72
  export default CannedResponsePanel;
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ interface BottomSheetProps {
3
+ open: boolean;
4
+ title: string;
5
+ onClose: () => void;
6
+ children: ReactNode;
7
+ }
8
+ /** Row style for actions inside a BottomSheet: 48px minimum hit area. */
9
+ export declare const BOTTOM_SHEET_ROW = "flex min-h-12 w-full items-center gap-3 rounded-lg px-3 text-left text-base text-gray-900 active:bg-gray-5";
10
+ /**
11
+ * After a sheet closes, rc-drawer refocuses its trigger; hand focus to a modal the sheet's
12
+ * action opened (one not in `alreadyOpen`), via a node rc-dialog makes focusable.
13
+ */
14
+ export declare const restoreModalFocus: (alreadyOpen?: ReadonlySet<HTMLElement>) => number;
15
+ /** CRM bottom sheet for phone actions: fixed 56px header, rows scroll within 85dvh. */
16
+ declare const BottomSheet: ({ open, title, onClose, children }: BottomSheetProps) => import("react/jsx-runtime").JSX.Element;
17
+ export default BottomSheet;
18
+ //# sourceMappingURL=BottomSheet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/components/common/BottomSheet.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI1D,UAAU,gBAAgB;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,+GACiF,CAAC;AAO/G;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,cAAa,WAAW,CAAC,WAAW,CAAa,WAW5C,CAAC;AAER,uFAAuF;AACvF,QAAA,MAAM,WAAW,GAAI,oCAAoC,gBAAgB,4CA0CxE,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ import { Drawer } from "antd";
5
+ import GroupPanelHeader from "../../components/group/GroupPanelHeader";
6
+ /** Row style for actions inside a BottomSheet: 48px minimum hit area. */
7
+ export const BOTTOM_SHEET_ROW = "flex min-h-12 w-full items-center gap-3 rounded-lg px-3 text-left text-base text-gray-900 active:bg-gray-5";
8
+ const visibleModalWraps = () => Array.from(document.querySelectorAll(".ant-modal-wrap")).filter((wrap) => wrap.style.display !== "none");
9
+ /**
10
+ * After a sheet closes, rc-drawer refocuses its trigger; hand focus to a modal the sheet's
11
+ * action opened (one not in `alreadyOpen`), via a node rc-dialog makes focusable.
12
+ */
13
+ export const restoreModalFocus = (alreadyOpen = new Set()) => window.setTimeout(() => {
14
+ var _a;
15
+ const wrap = visibleModalWraps()
16
+ .filter((candidate) => !alreadyOpen.has(candidate))
17
+ .pop();
18
+ if (!wrap || wrap.contains(document.activeElement))
19
+ return;
20
+ const target = (_a = wrap.querySelector('[role="dialog"] > [tabindex="0"]')) !== null && _a !== void 0 ? _a : wrap;
21
+ target.focus({ preventScroll: true });
22
+ }, 0);
23
+ /** CRM bottom sheet for phone actions: fixed 56px header, rows scroll within 85dvh. */
24
+ const BottomSheet = ({ open, title, onClose, children }) => {
25
+ const modalsAtOpen = useRef(new Set());
26
+ useEffect(() => {
27
+ if (open)
28
+ modalsAtOpen.current = new Set(visibleModalWraps());
29
+ }, [open]);
30
+ return (_jsxs(Drawer, { open: open, "aria-label": title, onClose: onClose, afterOpenChange: (opened) => {
31
+ if (!opened)
32
+ restoreModalFocus(modalsAtOpen.current);
33
+ }, placement: "bottom", height: "auto", closeIcon: false, destroyOnHidden: true, styles: {
34
+ mask: { background: "rgba(57, 62, 64, 0.45)" },
35
+ wrapper: {
36
+ borderTopLeftRadius: 12,
37
+ borderTopRightRadius: 12,
38
+ overflow: "hidden",
39
+ },
40
+ content: { maxHeight: "min(85dvh, var(--dc-visual-height, 85dvh))" },
41
+ body: {
42
+ padding: 0,
43
+ display: "flex",
44
+ flexDirection: "column",
45
+ minHeight: 0,
46
+ },
47
+ }, children: [_jsx(GroupPanelHeader, { title: title, onClose: onClose }), _jsx("div", { "data-desk-shortcuts-off": true, className: "min-h-0 flex-1 overflow-y-auto px-2 pt-1 pb-[max(12px,env(safe-area-inset-bottom))]", children: children })] }));
48
+ };
49
+ export default BottomSheet;
@@ -1 +1 @@
1
- {"version":3,"file":"DeskPanelDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/common/DeskPanelDrawer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,UAAU,oBAAoB;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,qFAAqF;IACrF,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,gHAAgH;AAChH,QAAA,MAAM,eAAe,GAAI,qEAQtB,oBAAoB,4CA+BtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"DeskPanelDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/common/DeskPanelDrawer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,UAAU,oBAAoB;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,qFAAqF;IACrF,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,gHAAgH;AAChH,QAAA,MAAM,eAAe,GAAI,qEAQtB,oBAAoB,4CA4CtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,21 +1,35 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Drawer } from "antd";
4
+ import useDeskPanelHostStore from "../../store/deskPanelHost";
4
5
  import { LAYOUT_WIDTHS } from "../../constants/layout";
5
6
  /** Overlay drawer for a desk side panel (inset 8px, rounded, light scrim). Sits below modals opened from it. */
6
- const DeskPanelDrawer = ({ open, label, onClose, container, autoFocus = true, keyboard = true, children, }) => (_jsx(Drawer, { open: open, "aria-label": label, onClose: onClose, autoFocus: autoFocus, keyboard: keyboard,
7
- // antd slides a drawer 180px aside when one opens inside it; panels here stack in place.
8
- push: false, destroyOnHidden: true, closeIcon: false, getContainer: container !== null && container !== void 0 ? container : false, rootStyle: { position: "absolute" }, zIndex: 900, width: LAYOUT_WIDTHS.infoDrawer, styles: {
9
- mask: { background: "rgba(57, 62, 64, 0.16)" },
10
- wrapper: {
11
- top: 8,
12
- right: 8,
13
- bottom: 8,
14
- height: "auto",
15
- borderRadius: 12,
16
- overflow: "hidden",
17
- boxShadow: "0 8px 24px rgba(57, 62, 64, 0.16)",
18
- },
19
- body: { padding: 0, background: "transparent" },
20
- }, children: children }));
7
+ const DeskPanelDrawer = ({ open, label, onClose, container, autoFocus = true, keyboard = true, children, }) => {
8
+ const isFullScreen = useDeskPanelHostStore((state) => state.isFullScreen);
9
+ return (_jsx(Drawer, { open: open, "aria-label": label, onClose: onClose, autoFocus: autoFocus, keyboard: keyboard,
10
+ // antd slides a drawer 180px aside when one opens inside it; panels here stack in place.
11
+ push: false, destroyOnHidden: true, closeIcon: false, getContainer: container !== null && container !== void 0 ? container : false, rootStyle: { position: "absolute" }, zIndex: 900, width: isFullScreen ? "100%" : LAYOUT_WIDTHS.infoDrawer, styles: {
12
+ mask: { background: "rgba(57, 62, 64, 0.16)" },
13
+ wrapper: isFullScreen
14
+ ? {
15
+ top: 0,
16
+ right: 0,
17
+ bottom: 0,
18
+ height: "auto",
19
+ borderRadius: 0,
20
+ overflow: "hidden",
21
+ boxShadow: "none",
22
+ }
23
+ : {
24
+ top: 8,
25
+ right: 8,
26
+ bottom: 8,
27
+ height: "auto",
28
+ borderRadius: 12,
29
+ overflow: "hidden",
30
+ boxShadow: "0 8px 24px rgba(57, 62, 64, 0.16)",
31
+ },
32
+ body: { padding: 0, background: "transparent" },
33
+ }, children: children }));
34
+ };
21
35
  export default DeskPanelDrawer;
@@ -1 +1 @@
1
- {"version":3,"file":"DeskSidePanel.d.ts","sourceRoot":"","sources":["../../../src/components/common/DeskSidePanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,UAAU,kBAAkB;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,QAAA,MAAM,aAAa,GAAI,gDAMpB,kBAAkB,mDAiCpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"DeskSidePanel.d.ts","sourceRoot":"","sources":["../../../src/components/common/DeskSidePanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,UAAU,kBAAkB;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,QAAA,MAAM,aAAa,GAAI,gDAMpB,kBAAkB,mDAmCpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -16,7 +16,7 @@ const DeskSidePanel = ({ open, label, onClose, autoFocus, children, }) => {
16
16
  : null;
17
17
  }
18
18
  if (host.workspace) {
19
- return (_jsx(DeskPanelDrawer, { open: open, label: label, onClose: onClose, container: host.workspace, autoFocus: autoFocus, children: _jsx("div", { className: "h-full overflow-hidden rounded-xl bg-white", children: children(false) }) }));
19
+ return (_jsx(DeskPanelDrawer, { open: open, label: label, onClose: onClose, container: host.workspace, autoFocus: autoFocus, children: _jsx("div", { className: `h-full overflow-hidden bg-white ${host.isFullScreen ? "" : "rounded-xl"}`, children: children(false) }) }));
20
20
  }
21
21
  return null;
22
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ComposeNewMessageModal.d.ts","sourceRoot":"","sources":["../../../src/components/compose/ComposeNewMessageModal.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID,QAAA,MAAM,sBAAsB,GAAI,oBAG7B,2BAA2B,4CAwO7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"ComposeNewMessageModal.d.ts","sourceRoot":"","sources":["../../../src/components/compose/ComposeNewMessageModal.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID,QAAA,MAAM,sBAAsB,GAAI,oBAG7B,2BAA2B,4CA2O7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -14,7 +14,7 @@ import CreateGroupForm from "../../components/group/CreateGroupForm";
14
14
  import useAuthStore from "../../store/auth";
15
15
  import { StrokeIcon } from "../../components/icon/StrokeIcon";
16
16
  import TintedAvatar from "../../components/common/TintedAvatar";
17
- import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
17
+ import { useDeskThreadNavigation } from "../../hooks/conversation/useDeskThreadNavigation";
18
18
  const ComposeNewMessageModal = ({ open, onClose, }) => {
19
19
  const { t } = useTranslation("conversation-inbox");
20
20
  const isCrm = useAuthStore((state) => state.isCrm);
@@ -24,7 +24,7 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
24
24
  const [openingUserID, setOpeningUserID] = useState(null);
25
25
  const debouncedSearch = useDebounce(search, { wait: 300 });
26
26
  const { accounts, isLoading } = useEligibleAccounts(debouncedSearch, open);
27
- const onNavigate = useChatNavigate();
27
+ const { openThread } = useDeskThreadNavigation();
28
28
  const setConversationData = useConversationStore((state) => state.setConversationData);
29
29
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
30
30
  const handleClose = () => {
@@ -53,7 +53,7 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
53
53
  newSearchParams.delete("userId");
54
54
  newSearchParams.delete("groupId");
55
55
  newSearchParams.delete("botId");
56
- onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
56
+ openThread({ threadId: conversation.conversationID }, `${window.location.pathname}?${newSearchParams.toString()}`);
57
57
  handleClose();
58
58
  }
59
59
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAaxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CAoE9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAaxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CA8D9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import useConversationStore from "../../store/conversation";
5
5
  import { useChatContext } from "../../context/ChatContext";
6
- import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
6
+ import { useDeskThreadNavigation } from "../../hooks/conversation/useDeskThreadNavigation";
7
7
  import { parseLatestMessage } from "../../utils/common";
8
8
  import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
9
9
  import DeskConversationRow from "./DeskConversationRow";
@@ -15,7 +15,7 @@ import MentionIndicatorIcon from "./MentionIndicatorIcon";
15
15
  const ConversationByGroupItem = ({ group, latestPreview, }) => {
16
16
  const { t } = useTranslation();
17
17
  const { user } = useChatContext();
18
- const onNavigate = useChatNavigate();
18
+ const { openFromList } = useDeskThreadNavigation();
19
19
  const setConversationData = useConversationStore((state) => state.setConversationData);
20
20
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
21
21
  const isSelected = useConversationStore((state) => state.selectedConversationId === group.conversationId);
@@ -23,12 +23,7 @@ const ConversationByGroupItem = ({ group, latestPreview, }) => {
23
23
  const handleClick = () => {
24
24
  setConversationData(conversation);
25
25
  setSelectedConversationId(conversation.conversationID);
26
- const newSearchParams = new URLSearchParams(window.location.search);
27
- newSearchParams.set("threadId", conversation.conversationID);
28
- newSearchParams.delete("userId");
29
- newSearchParams.delete("groupId");
30
- newSearchParams.delete("botId");
31
- onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
26
+ openFromList({ threadId: conversation.conversationID });
32
27
  };
33
28
  const latestMsg = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsg) || "";
34
29
  const latestMsgSendTime = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsgSendTime) || 0;
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQhD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU1C,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qFAAqF;IACrF,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB;AAaD,QAAA,MAAM,yBAAyB,GAAI,4CAIhC,8BAA8B,mDAsGhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQhD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU1C,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qFAAqF;IACrF,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB;AAaD,QAAA,MAAM,yBAAyB,GAAI,4CAIhC,8BAA8B,mDAiGhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -4,7 +4,7 @@ import { PeerType } from "../../types/conversation";
4
4
  import { useTranslation } from "react-i18next";
5
5
  import useConversationStore from "../../store/conversation";
6
6
  import { useChatContext } from "../../context/ChatContext";
7
- import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
7
+ import { useDeskThreadNavigation } from "../../hooks/conversation/useDeskThreadNavigation";
8
8
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
9
9
  import { isGroupSession } from "../../utils/imCommon";
10
10
  import { resolveBotSessionOwnerIdentity } from "../../utils/conversation";
@@ -29,7 +29,7 @@ const ConversationBySessionItem = ({ sessionItem, latestPreview, folderTag, }) =
29
29
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
30
30
  const { t } = useTranslation();
31
31
  const { user } = useChatContext();
32
- const onNavigate = useChatNavigate();
32
+ const { openFromList } = useDeskThreadNavigation();
33
33
  const isSelected = useConversationStore((state) => {
34
34
  var _a;
35
35
  return state.selectedConversationId ===
@@ -40,12 +40,7 @@ const ConversationBySessionItem = ({ sessionItem, latestPreview, folderTag, }) =
40
40
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
41
41
  const handleConversationClick = (conversation) => {
42
42
  setConversationData(conversation);
43
- const newSearchParams = new URLSearchParams(window.location.search);
44
- newSearchParams.set("threadId", conversation.conversationID);
45
- newSearchParams.delete("userId");
46
- newSearchParams.delete("groupId");
47
- newSearchParams.delete("botId");
48
- onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
43
+ openFromList({ threadId: conversation.conversationID });
49
44
  setSelectedConversationId(conversation.conversationID);
50
45
  };
51
46
  const { avatar, displayName = "" } = useConversationDisplayData(conversation || null);
@@ -1,3 +1,6 @@
1
- declare const DeskConversationList: () => import("react/jsx-runtime").JSX.Element;
1
+ interface DeskConversationListProps {
2
+ onOpenAppMenu?: (opener: HTMLElement | null) => void;
3
+ }
4
+ declare const DeskConversationList: ({ onOpenAppMenu }: DeskConversationListProps) => import("react/jsx-runtime").JSX.Element;
2
5
  export default DeskConversationList;
3
6
  //# sourceMappingURL=DeskConversationList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA+BA,QAAA,MAAM,oBAAoB,+CAgRzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA+BA,UAAU,yBAAyB;IACjC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CACtD;AAED,QAAA,MAAM,oBAAoB,GAAI,mBAAmB,yBAAyB,4CAiRzE,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -27,7 +27,7 @@ import DeskListSkeleton from "./DeskListSkeleton";
27
27
  import { getListHeading } from "./listHeading";
28
28
  import ComposeNewMessageModal from "../../components/compose/ComposeNewMessageModal";
29
29
  import { useCloseOnSidePanelOpen } from "../../hooks/common/useCloseOnSidePanelOpen";
30
- const DeskConversationList = () => {
30
+ const DeskConversationList = ({ onOpenAppMenu }) => {
31
31
  const searchInputRef = useRef(null);
32
32
  const { t } = useTranslation();
33
33
  const [search, setSearch] = useState("");
@@ -150,7 +150,7 @@ const DeskConversationList = () => {
150
150
  if (!showSearch && value)
151
151
  setShowSearchTrue();
152
152
  setSearch(value);
153
- }, onSearchFocus: onSearchFocus, onCloseSearch: onCloseSearch, onCompose: () => setIsComposeOpen(true) }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", "aria-busy": isListLoading, style: {
153
+ }, onSearchFocus: onSearchFocus, onCloseSearch: onCloseSearch, onCompose: () => setIsComposeOpen(true), onOpenAppMenu: onOpenAppMenu }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", "aria-busy": isListLoading, style: {
154
154
  height: "100%",
155
155
  overflow: showSearch ? "hidden" : "auto",
156
156
  }, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx(DeskListSkeleton, { rows: 8 })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_jsx(DeskListEmptyState, { filterKey: emptyFilterKey })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx(DeskListSkeleton, { rows: 2 }), scrollableTarget: "scrollableConversationsDiv", children: _jsx("div", { className: "flex flex-col gap-0.5 px-1 pb-2", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }) }))) : isSessionsLoading || (isSyncing && !(sessions === null || sessions === void 0 ? void 0 : sessions.length)) ? (
@@ -7,6 +7,8 @@ interface DeskListHeaderProps {
7
7
  onSearchFocus: () => void;
8
8
  onCloseSearch: () => void;
9
9
  onCompose: () => void;
10
+ /** Phone only: ☰ that opens the host app menu. */
11
+ onOpenAppMenu?: (opener: HTMLElement | null) => void;
10
12
  }
11
13
  /** Search row plus a title that names the folder the list below is built from. */
12
14
  declare const DeskListHeader: import("react").ForwardRefExoticComponent<DeskListHeaderProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"DeskListHeader.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskListHeader.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,UAAU,mBAAmB;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,kFAAkF;AAClF,QAAA,MAAM,cAAc,kHAkJnB,CAAC;AAGF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"DeskListHeader.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskListHeader.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,UAAU,mBAAmB;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kDAAkD;IAClD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CACtD;AAED,kFAAkF;AAClF,QAAA,MAAM,cAAc,kHA8JnB,CAAC;AAGF,eAAe,cAAc,CAAC"}
@@ -9,7 +9,7 @@ import useWorkspaceLayoutStore from "../../store/workspaceLayout";
9
9
  import useShortcutPrefsStore from "../../store/shortcutPrefs";
10
10
  import { LAYOUT_BREAKPOINTS } from "../../constants/layout";
11
11
  /** Search row plus a title that names the folder the list below is built from. */
12
- const DeskListHeader = forwardRef(({ heading, search, isSearching, onSearchChange, onSearchFocus, onCloseSearch, onCompose, }, inputRef) => {
12
+ const DeskListHeader = forwardRef(({ heading, search, isSearching, onSearchChange, onSearchFocus, onCloseSearch, onCompose, onOpenAppMenu, }, inputRef) => {
13
13
  var _a, _b;
14
14
  const { t } = useTranslation();
15
15
  const [isFocused, setIsFocused] = useState(false);
@@ -18,7 +18,7 @@ const DeskListHeader = forwardRef(({ heading, search, isSearching, onSearchChang
18
18
  const singleKeyEnabled = useShortcutPrefsStore((s) => s.singleKeyEnabled);
19
19
  const context = (_a = heading.contextText) !== null && _a !== void 0 ? _a : t((_b = heading.contextKey) !== null && _b !== void 0 ? _b : "my_messages");
20
20
  const showSlashHint = singleKeyEnabled && !isFocused && !search && !isSearching;
21
- return (_jsxs("div", { "data-cp-name": "DeskListHeader", children: [_jsxs("div", { className: "flex items-center gap-2 p-3", children: [!isFolderInline && (_jsx(Tooltip, { title: t("folder_panel_open"), children: _jsx(Button, { onClick: () => setFolderOverlayOpen(true), className: "flex h-9 w-9 shrink-0 items-center justify-center border-0 bg-gray-5 p-0 text-gray-900", "aria-label": t("folder_panel_open"), children: _jsx(StrokeIcon, { name: "panel-left", size: 20 }) }) })), _jsx("label", { htmlFor: "desk-list-search", className: "sr-only", children: t("search_conversation_label") }), _jsxs("div", { className: "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-lg bg-gray-5 pl-2.5 pr-2 text-gray-600 focus-within:ring-2 focus-within:ring-primary-200", children: [_jsx(StrokeIcon, { name: "search", size: 18 }), _jsx("input", { id: "desk-list-search", ref: inputRef, type: "text", "data-desk-search": true, autoComplete: "off",
21
+ return (_jsxs("div", { "data-cp-name": "DeskListHeader", children: [_jsxs("div", { className: "flex items-center gap-2 p-3", children: [onOpenAppMenu && (_jsx("button", { type: "button", "data-app-menu-button": true, onClick: (event) => onOpenAppMenu(event.currentTarget), className: "hidden phone:flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border-0 bg-transparent p-0 text-gray-900 touch:min-h-10 touch:min-w-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", "aria-label": t("app_menu_open"), children: _jsx(StrokeIcon, { name: "menu", size: 20 }) })), !isFolderInline && (_jsx(Tooltip, { title: t("folder_panel_open"), children: _jsx(Button, { onClick: () => setFolderOverlayOpen(true), className: "flex h-9 w-9 shrink-0 items-center justify-center border-0 bg-gray-5 p-0 text-gray-900 max-lg:!hidden", "aria-label": t("folder_panel_open"), children: _jsx(StrokeIcon, { name: "panel-left", size: 20 }) }) })), _jsx("label", { htmlFor: "desk-list-search", className: "sr-only", children: t("search_conversation_label") }), _jsxs("div", { className: "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-lg bg-gray-5 pl-2.5 pr-2 text-gray-600 focus-within:ring-2 focus-within:ring-primary-200", children: [_jsx(StrokeIcon, { name: "search", size: 18 }), _jsx("input", { id: "desk-list-search", ref: inputRef, type: "text", "data-desk-search": true, autoComplete: "off",
22
22
  // The narrow list (folder toggle shown) has no room for the long hint.
23
23
  placeholder: t(isFolderInline ? "desk_search_placeholder" : "search_conversation_label"), value: search, onChange: (e) => onSearchChange(e.target.value), onFocus: () => {
24
24
  setIsFocused(true);
@@ -33,7 +33,7 @@ const DeskListHeader = forwardRef(({ heading, search, isSearching, onSearchChang
33
33
  e.stopPropagation();
34
34
  onCloseSearch();
35
35
  }
36
- }, className: "min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-gray-900 outline-none placeholder:text-gray-600" }), search && (_jsx("button", { type: "button", "aria-label": t("clear"), onClick: onCloseSearch, className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-gray-600 hover:text-gray-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: _jsx(StrokeIcon, { name: "x", size: 14 }) })), showSlashHint && (_jsx("kbd", { "aria-hidden": "true", className: "inline-flex h-6 min-w-[24px] items-center justify-center rounded border border-b-2 border-gray-50 bg-white px-1.5 font-sans text-xs font-semibold text-gray-900", children: "/" }))] }), isSearching ? (_jsx(Tooltip, { title: t("close"), children: _jsx(Button, { onClick: onCloseSearch, "aria-label": t("close"), className: "h-9 w-9 shrink-0 p-0 text-gray-600", children: _jsx(StrokeIcon, { name: "x", size: 20 }) }) })) : (_jsx(Tooltip, { title: t("compose_new_message"), children: _jsx(Button, { "data-testid": "conversation-list-compose-icon", onClick: onCompose, "aria-label": t("compose_new_message"), className: "flex !h-8 !w-8 shrink-0 items-center justify-center rounded-lg border-0 bg-primary-25 p-0 text-primary-500", children: _jsx(StrokeIcon, { name: "edit", size: 18 }) }) }))] }), !isSearching && (_jsxs("div", { className: "flex flex-col gap-0.5 border-b border-gray-25 px-4 pb-3 pt-1", children: [isFolderInline ? (_jsx("span", { className: "truncate text-xs font-medium text-gray-600", children: context })) : (_jsxs("button", { type: "button", "aria-label": `${t("folder_panel_open")}: ${context}`, onClick: () => setFolderOverlayOpen(true), className: "flex min-w-0 items-center gap-1 self-start rounded border-0 bg-transparent p-0 text-xs font-medium leading-[1.4] text-gray-600 hover:text-gray-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: [_jsx("span", { className: "truncate", children: context }), _jsx(StrokeIcon, { name: "chevron-down", size: 14 })] })), _jsxs("h2", { className: "m-0 flex min-w-0 items-center gap-2 text-lg font-bold text-gray-900", children: [_jsx("span", { className: "truncate", children: t(heading.titleKey) }), " ", !!heading.count && (_jsx("span", { className: "inline-flex h-6 shrink-0 items-center rounded-full bg-primary-25 px-2 text-sm font-semibold tabular-nums text-primary-500", children: heading.count }))] })] }))] }));
36
+ }, className: "min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-gray-900 outline-none placeholder:text-gray-600" }), search && (_jsx("button", { type: "button", "aria-label": t("clear"), onClick: onCloseSearch, className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-gray-600 hover:text-gray-900 touch:min-h-8 touch:min-w-8 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: _jsx(StrokeIcon, { name: "x", size: 14 }) })), showSlashHint && (_jsx("kbd", { "aria-hidden": "true", className: "touch:hidden inline-flex h-6 min-w-[24px] items-center justify-center rounded border border-b-2 border-gray-50 bg-white px-1.5 font-sans text-xs font-semibold text-gray-900", children: "/" }))] }), isSearching ? (_jsx(Tooltip, { title: t("close"), children: _jsx(Button, { onClick: onCloseSearch, "aria-label": t("close"), className: "h-9 w-9 shrink-0 p-0 text-gray-600 touch:min-h-10 touch:min-w-10", children: _jsx(StrokeIcon, { name: "x", size: 20 }) }) })) : (_jsx(Tooltip, { title: t("compose_new_message"), children: _jsx(Button, { "data-testid": "conversation-list-compose-icon", onClick: onCompose, "aria-label": t("compose_new_message"), className: "flex !h-8 !w-8 shrink-0 items-center justify-center rounded-lg border-0 bg-primary-25 p-0 text-primary-500 touch:!min-h-10 touch:!min-w-10", children: _jsx(StrokeIcon, { name: "edit", size: 18 }) }) }))] }), !isSearching && (_jsxs("div", { className: "flex flex-col gap-0.5 border-b border-gray-25 px-4 pb-3 pt-1", children: [isFolderInline ? (_jsx("span", { className: "truncate text-xs font-medium text-gray-600", children: context })) : (_jsxs("button", { type: "button", "aria-label": `${t("folder_panel_open")}: ${context}`, onClick: () => setFolderOverlayOpen(true), className: "flex min-w-0 items-center gap-1 self-start rounded border-0 bg-transparent p-0 touch:-my-3 touch:py-3 text-xs font-medium leading-[1.4] text-gray-600 hover:text-gray-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: [_jsx("span", { className: "truncate", children: context }), _jsx(StrokeIcon, { name: "chevron-down", size: 14 })] })), _jsxs("h2", { className: "m-0 flex min-w-0 items-center gap-2 text-lg font-bold text-gray-900", children: [_jsx("span", { className: "truncate", children: t(heading.titleKey) }), " ", !!heading.count && (_jsx("span", { className: "inline-flex h-6 shrink-0 items-center rounded-full bg-primary-25 px-2 text-sm font-semibold tabular-nums text-primary-500", children: heading.count }))] })] }))] }));
37
37
  });
38
38
  DeskListHeader.displayName = "DeskListHeader";
39
39
  export default DeskListHeader;
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,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,4CAoS9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,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,4CAsS9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -158,7 +158,7 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
158
158
  target: scrollRef,
159
159
  enable: !!openToolboxMessageId,
160
160
  });
161
- return (_jsxs("div", { "data-testid": "conversation-message-list", "data-cp-name": "ConversationMessageList", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: [isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [_jsx("div", { "aria-hidden": "true", className: "h-3 shrink-0" }), bottomSlot, _jsx(ConsultantCtaContext.Provider, { value: consultantCta, children: loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: setQuotedMessage, onRevokeMessage: handleRevokeMessage, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, onCopyMessageLink: handleCopyMessageLink, showUnreadDivider: !!unreadDividerMessageId &&
161
+ return (_jsxs("div", { "data-testid": "conversation-message-list", "data-cp-name": "ConversationMessageList", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: [isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [_jsx("div", { "aria-hidden": "true", className: "h-3 shrink-0" }), bottomSlot, _jsx(ConsultantCtaContext.Provider, { value: consultantCta, children: loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: setQuotedMessage, onRevokeMessage: handleRevokeMessage, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, onCopyMessageLink: onCopyMessageLink ? handleCopyMessageLink : undefined, showUnreadDivider: !!unreadDividerMessageId &&
162
162
  message.clientMsgID === unreadDividerMessageId, onUnreadDividerSeen: markDividerSeen, enableLongPress: true }, message.clientMsgID))) })] })), !isMessageLoading && showJumpButton && (_jsx(UnreadMessageJumpButton, { className: "absolute right-0 top-4 z-10", onClick: handleJumpToFirstUnread })), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
163
163
  };
164
164
  export default ConversationMessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationInboxList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAMjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAYD,QAAA,MAAM,qBAAqB,GAAI,oFAO5B,0BAA0B,4CAkF5B,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ConversationInboxList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAYjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAYD,QAAA,MAAM,qBAAqB,GAAI,oFAO5B,0BAA0B,4CA8F5B,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -1,16 +1,23 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
- import { Empty, Spin } from "antd";
4
+ import { Empty } from "antd";
5
5
  import InfiniteScroll from "react-infinite-scroll-component";
6
6
  import useConversationStore from "../../store/conversation";
7
7
  import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
8
8
  import { Icon } from "../../components/icon";
9
+ import useBootStore from "../../store/boot";
10
+ import { useChatBootPhase } from "../../hooks/init/useChatBootPhase";
9
11
  import ConversationInboxItem from "./ConversationInboxItem";
12
+ import InboxBootStatus from "./InboxBootStatus";
13
+ import InboxListSkeleton from "./InboxListSkeleton";
14
+ const INBOX_SLOW_AFTER_MS = 8000;
10
15
  const FooterSkeleton = () => (_jsxs("div", { className: "flex items-center gap-3 p-3", children: [_jsx("div", { className: "h-10 w-10 animate-pulse rounded-full bg-neutral-200" }), _jsxs("div", { className: "flex flex-1 flex-col gap-2", children: [_jsx("div", { className: "h-3 w-1/2 animate-pulse rounded bg-neutral-200" }), _jsx("div", { className: "h-2.5 w-1/3 animate-pulse rounded bg-neutral-200" })] })] }));
11
16
  const ConversationInboxList = ({ items, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, onSelectThread, }) => {
12
17
  const { t } = useTranslation("conversation-inbox");
13
18
  const onNavigate = useChatNavigate();
19
+ const boot = useChatBootPhase({ slowAfterMs: INBOX_SLOW_AFTER_MS });
20
+ const isListSettled = useBootStore((state) => state.isListSettled);
14
21
  const setConversationData = useConversationStore((state) => state.setConversationData);
15
22
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
16
23
  const handleSelectThread = (item) => {
@@ -29,8 +36,10 @@ const ConversationInboxList = ({ items, isLoading, isFetchingNextPage, hasNextPa
29
36
  onLoadMore();
30
37
  }
31
38
  };
32
- if (isLoading) {
33
- return (_jsx("div", { "data-testid": "conversation-inbox-thread-list", className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
39
+ // Until the list has loaded once after the first sync, an empty list means "not loaded yet".
40
+ if (items.length === 0 && (!isListSettled || isLoading)) {
41
+ return (_jsxs("div", { "data-testid": "conversation-inbox-thread-list", className: "min-h-0 w-full flex-1 overflow-hidden", children: [!isListSettled && (_jsx(InboxBootStatus, { boot: boot.phase === "ready"
42
+ ? Object.assign(Object.assign({}, boot), { phase: "sync", syncProgress: 100 }) : boot })), boot.phase !== "error" && _jsx(InboxListSkeleton, { rows: 6 })] }));
34
43
  }
35
44
  return (_jsx("div", { id: "scrollableConversationThreadsDiv", "data-testid": "conversation-inbox-thread-list", "data-cp-name": "ConversationInboxList", className: "min-h-0 w-full flex-1 overflow-auto", children: _jsx(InfiniteScroll, { dataLength: items.length, next: handleEndReached, hasMore: hasNextPage, loader: isFetchingNextPage ? _jsx(FooterSkeleton, {}) : null, scrollableTarget: "scrollableConversationThreadsDiv", children: items.length === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx("div", { className: "flex justify-center", children: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-[var(--dc-color-primary-light)]" }) }), description: t("empty.no_conversations") }) })) : (items.map((item) => (_jsx(ConversationInboxItem, { item: item, onClick: handleSelectThread }, item.conversationID)))) }) }));
36
45
  };
@@ -0,0 +1,10 @@
1
+ import type { ChatBootState } from "../../hooks/init/useChatBootPhase";
2
+ /**
3
+ * "Đang tải tin nhắn" block shown above the inbox skeleton until the list first loads.
4
+ * Three bars, one per boot step; no percentage text (PO decision for end-customer apps).
5
+ */
6
+ declare const InboxBootStatus: ({ boot }: {
7
+ boot: ChatBootState;
8
+ }) => import("react/jsx-runtime").JSX.Element | null;
9
+ export default InboxBootStatus;
10
+ //# sourceMappingURL=InboxBootStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InboxBootStatus.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/InboxBootStatus.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAyCnE;;;GAGG;AACH,QAAA,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,mDAmDzD,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { Button } from "antd";
5
+ import { useTranslation } from "react-i18next";
6
+ import BootProgressBar from "../../components/boot/BootProgressBar";
7
+ const SEGMENTS = ["engine", "connect", "sync"];
8
+ const DOT_DELAYS = ["0ms", "160ms", "320ms"];
9
+ const segmentMode = (segment, boot) => {
10
+ const current = SEGMENTS.indexOf(boot.phase);
11
+ const index = SEGMENTS.indexOf(segment);
12
+ if (index < current)
13
+ return "complete";
14
+ if (index > current)
15
+ return "pending";
16
+ // Core only reports sync progress on a fresh device; otherwise the last bar just runs.
17
+ return segment === "sync" && boot.syncProgress > 0 ? "progress" : "indeterminate";
18
+ };
19
+ /** Chat bubble with the "typing" dots while loading, or a "!" when loading failed. */
20
+ const TypingBubble = ({ isError }) => (_jsxs("div", { "aria-hidden": "true", className: clsx("relative mb-2 flex h-[50px] w-16 items-center justify-center gap-1.5 rounded-2xl", isError ? "bg-danger-25" : "bg-[var(--dc-color-primary-light)]"), children: [_jsx("span", { className: "absolute -bottom-1.5 left-3 h-3.5 w-3.5 rotate-45 rounded-sm bg-inherit" }), isError ? (_jsx("span", { className: "relative text-xl font-bold leading-none text-danger-500", children: "!" })) : (DOT_DELAYS.map((delay) => (_jsx("span", { className: "relative h-2 w-2 rounded-full bg-[var(--dc-color-primary)] motion-safe:animate-[dchat-boot-typing_1.2s_ease-in-out_infinite]", style: { animationDelay: delay } }, delay))))] }));
21
+ /**
22
+ * "Đang tải tin nhắn" block shown above the inbox skeleton until the list first loads.
23
+ * Three bars, one per boot step; no percentage text (PO decision for end-customer apps).
24
+ */
25
+ const InboxBootStatus = ({ boot }) => {
26
+ const { t } = useTranslation("boot");
27
+ if (boot.phase === "ready")
28
+ return null;
29
+ const isError = boot.phase === "error";
30
+ const title = isError ? t("inbox.error_title") : t("inbox.title");
31
+ const detail = isError ? t("inbox.error_hint") : t(`inbox.${boot.phase}`);
32
+ return (_jsxs("div", { "data-cp-name": "InboxBootStatus", "data-phase": boot.phase, className: clsx("flex flex-col items-center gap-2 px-6 pb-4 pt-7 text-center transition-opacity duration-200", !boot.isStatusVisible && "opacity-0"), children: [_jsx("p", { role: "status", "aria-live": "polite", className: "sr-only", children: boot.isStatusVisible ? `${title}. ${detail}` : "" }), _jsx(TypingBubble, { isError: isError }), _jsx("p", { "aria-hidden": "true", className: "text-base font-semibold text-gray-900", children: title }), _jsx("p", { "aria-hidden": "true", className: "min-h-[1.5em] text-sm text-gray-500", children: detail }), isError ? (_jsx(Button, { type: "primary", className: "mt-2", onClick: () => window.location.reload(), children: t("retry") })) : (_jsx("div", { className: "mt-1 flex w-[168px] max-w-full gap-1", children: SEGMENTS.map((segment) => (_jsx(BootProgressBar, { className: "flex-1", mode: segmentMode(segment, boot), value: boot.syncProgress, label: t("sync_progress_label") }, segment))) })), boot.isSlow && !isError && (_jsx("p", { className: "mt-1.5 rounded-lg bg-warning-25 px-3 py-2 text-[13px] leading-normal text-warning-900", children: t("inbox.slow") }))] }));
33
+ };
34
+ export default InboxBootStatus;
@@ -0,0 +1,6 @@
1
+ /** Placeholder rows shaped like the inbox ThreadCard, shown until the list has loaded once. */
2
+ declare const InboxListSkeleton: ({ rows }: {
3
+ rows: number;
4
+ }) => import("react/jsx-runtime").JSX.Element;
5
+ export default InboxListSkeleton;
6
+ //# sourceMappingURL=InboxListSkeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InboxListSkeleton.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/InboxListSkeleton.tsx"],"names":[],"mappings":"AAOA,+FAA+F;AAC/F,QAAA,MAAM,iBAAiB,GAAI,UAAU;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,4CAyBpD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ const TITLE_WIDTHS = ["w-2/5", "w-1/2", "w-1/3"];
5
+ const PREVIEW_WIDTHS = ["w-4/5", "w-3/5", "w-2/3"];
6
+ /** Placeholder rows shaped like the inbox ThreadCard, shown until the list has loaded once. */
7
+ const InboxListSkeleton = ({ rows }) => (_jsx("div", { "aria-hidden": "true", "data-testid": "inbox-list-skeleton", children: Array.from({ length: rows }, (_, index) => (_jsxs("div", { className: "flex items-start gap-2 border-b border-gray-25 p-3", children: [_jsx("span", { className: "h-12 w-12 shrink-0 rounded-full bg-neutral-200 motion-safe:animate-pulse" }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-2 pt-1", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { className: clsx("h-3.5 rounded bg-neutral-200 motion-safe:animate-pulse", TITLE_WIDTHS[index % TITLE_WIDTHS.length]) }), _jsx("span", { className: "ml-auto h-3 w-9 rounded bg-neutral-200 motion-safe:animate-pulse" })] }), _jsx("span", { className: clsx("h-3 rounded bg-neutral-200 motion-safe:animate-pulse", PREVIEW_WIDTHS[index % PREVIEW_WIDTHS.length]) })] })] }, index))) }));
8
+ export default InboxListSkeleton;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { CrmIcon as Icon } from "../../components/group/crmIcon";
5
- const ICON_BUTTON = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
5
+ const ICON_BUTTON = "flex h-8 w-8 touch:min-h-10 touch:min-w-10 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
6
6
  /** CRM side-panel header: 56px bar with an optional back button and a close button. */
7
7
  const GroupPanelHeader = ({ title, onClose, onBack, backLabel, }) => {
8
8
  const { t } = useTranslation();