@droppii-org/chat-sdk 0.8.1 → 0.8.3

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 (191) hide show
  1. package/dist/components/canned-response/cannedTrigger.d.ts +2 -0
  2. package/dist/components/canned-response/cannedTrigger.d.ts.map +1 -0
  3. package/dist/components/canned-response/cannedTrigger.js +3 -0
  4. package/dist/components/canned-response/insertCannedContent.js +2 -2
  5. package/dist/components/conversation/ConversationByGroupItem.js +1 -1
  6. package/dist/components/conversation/ConversationBySessionItem.js +4 -4
  7. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  8. package/dist/components/conversation/DeskConversationList.js +6 -4
  9. package/dist/components/conversation/folderEmptyState.d.ts.map +1 -1
  10. package/dist/components/conversation/folderEmptyState.js +2 -0
  11. package/dist/components/conversation/internalNotePreview.d.ts +1 -1
  12. package/dist/components/conversation/internalNotePreview.d.ts.map +1 -1
  13. package/dist/components/conversation/internalNotePreview.js +22 -3
  14. package/dist/components/conversation/listHeading.d.ts +1 -1
  15. package/dist/components/conversation/listHeading.d.ts.map +1 -1
  16. package/dist/components/conversation/listHeading.js +7 -1
  17. package/dist/components/conversation/rowTime.d.ts +2 -2
  18. package/dist/components/conversation/rowTime.d.ts.map +1 -1
  19. package/dist/components/conversation/rowTime.js +2 -4
  20. package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
  21. package/dist/components/conversation-detail/ConversationDetailInput.js +8 -3
  22. package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
  23. package/dist/components/conversation-detail/ConversationMessageList.js +6 -3
  24. package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
  25. package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
  26. package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
  27. package/dist/components/conversation-inbox/ConversationInboxList.js +4 -0
  28. package/dist/components/conversation-inbox/InboxBootStatus.d.ts.map +1 -1
  29. package/dist/components/conversation-inbox/InboxBootStatus.js +10 -2
  30. package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
  31. package/dist/components/group/CreateGroupForm.js +2 -1
  32. package/dist/components/group/CreateGroupFormCrmView.d.ts.map +1 -1
  33. package/dist/components/group/CreateGroupFormCrmView.js +1 -1
  34. package/dist/components/group/GroupMemberPickerCrm.d.ts.map +1 -1
  35. package/dist/components/group/GroupMemberPickerCrm.js +1 -1
  36. package/dist/components/media-collection/FileCollection.d.ts.map +1 -1
  37. package/dist/components/media-collection/FileCollection.js +4 -3
  38. package/dist/components/media-collection/ImageCollection.d.ts.map +1 -1
  39. package/dist/components/media-collection/ImageCollection.js +2 -1
  40. package/dist/components/media-collection/LinkCollection.d.ts.map +1 -1
  41. package/dist/components/media-collection/LinkCollection.js +14 -26
  42. package/dist/components/media-collection/VideoCollection.d.ts.map +1 -1
  43. package/dist/components/media-collection/VideoCollection.js +2 -1
  44. package/dist/components/media-resources/FileResourceList.js +2 -2
  45. package/dist/components/media-resources/ResourceScrollContainer.d.ts.map +1 -1
  46. package/dist/components/media-resources/ResourceScrollContainer.js +34 -2
  47. package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
  48. package/dist/components/message/GroupMembersDrawer.js +2 -3
  49. package/dist/components/message/MessageList.d.ts.map +1 -1
  50. package/dist/components/message/MessageList.js +15 -4
  51. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  52. package/dist/components/message/footer/ActionBar.js +10 -44
  53. package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -1
  54. package/dist/components/message/footer/CannedResponsePlugin.js +3 -2
  55. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  56. package/dist/components/message/footer/EnterHandler.js +10 -47
  57. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  58. package/dist/components/message/footer/FilePreview.js +2 -1
  59. package/dist/components/message/footer/LinkInputPreview.d.ts +1 -1
  60. package/dist/components/message/footer/LinkInputPreview.d.ts.map +1 -1
  61. package/dist/components/message/footer/LinkInputPreview.js +8 -1
  62. package/dist/components/message/footer/MediaUploadButtons.d.ts.map +1 -1
  63. package/dist/components/message/footer/MediaUploadButtons.js +4 -7
  64. package/dist/components/message/footer/index.d.ts.map +1 -1
  65. package/dist/components/message/footer/index.js +57 -16
  66. package/dist/components/message/footer/mention/useMentionOptions.d.ts.map +1 -1
  67. package/dist/components/message/footer/mention/useMentionOptions.js +2 -6
  68. package/dist/components/message/footer/resetEditorAfterSend.d.ts +7 -0
  69. package/dist/components/message/footer/resetEditorAfterSend.d.ts.map +1 -0
  70. package/dist/components/message/footer/resetEditorAfterSend.js +62 -0
  71. package/dist/components/message/groupMemberMenuCrm.d.ts +4 -4
  72. package/dist/components/message/groupMemberMenuCrm.d.ts.map +1 -1
  73. package/dist/components/message/groupMemberMenuCrm.js +73 -60
  74. package/dist/components/message/item/FileMessage.js +2 -2
  75. package/dist/components/message/item/SystemLogMessage.d.ts.map +1 -1
  76. package/dist/components/message/item/SystemLogMessage.js +5 -2
  77. package/dist/components/search-conversation/SearchConversationAsGroups.js +1 -1
  78. package/dist/components/search-conversation/SearchConversationAsMessages.js +1 -1
  79. package/dist/components/search-conversation/SearchConversationMyInbox.js +1 -1
  80. package/dist/components/session/SessionFolderList.d.ts +4 -3
  81. package/dist/components/session/SessionFolderList.d.ts.map +1 -1
  82. package/dist/components/session/SessionFolderList.js +14 -5
  83. package/dist/components/thread/GroupInfoBodyCrm.d.ts.map +1 -1
  84. package/dist/components/thread/GroupInfoBodyCrm.js +2 -2
  85. package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
  86. package/dist/components/thread/GroupThreadInfo.js +2 -6
  87. package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -1
  88. package/dist/components/thread/ManualAssignPopover.js +6 -8
  89. package/dist/components/thread/SessionDetailCard.d.ts.map +1 -1
  90. package/dist/components/thread/SessionDetailCard.js +3 -3
  91. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  92. package/dist/components/thread/SessionSection.js +3 -9
  93. package/dist/components/thread/SessionTagPicker.d.ts +3 -1
  94. package/dist/components/thread/SessionTagPicker.d.ts.map +1 -1
  95. package/dist/components/thread/SessionTagPicker.js +2 -2
  96. package/dist/components/thread/sessionDateFormat.d.ts +2 -0
  97. package/dist/components/thread/sessionDateFormat.d.ts.map +1 -1
  98. package/dist/components/thread/sessionDateFormat.js +6 -0
  99. package/dist/constants/dateFormat.d.ts +2 -0
  100. package/dist/constants/dateFormat.d.ts.map +1 -0
  101. package/dist/constants/dateFormat.js +2 -0
  102. package/dist/hooks/common/useFetchUrlMetadata.d.ts +1 -0
  103. package/dist/hooks/common/useFetchUrlMetadata.d.ts.map +1 -1
  104. package/dist/hooks/conversation/useBotFlow.d.ts.map +1 -1
  105. package/dist/hooks/conversation/useBotFlow.js +64 -12
  106. package/dist/hooks/conversation/useCloseBotSession.d.ts +2 -0
  107. package/dist/hooks/conversation/useCloseBotSession.d.ts.map +1 -1
  108. package/dist/hooks/conversation/useCloseBotSession.js +17 -1
  109. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  110. package/dist/hooks/global/useGlobalEvent.js +148 -16
  111. package/dist/hooks/global/useLoginHealth.d.ts +3 -0
  112. package/dist/hooks/global/useLoginHealth.d.ts.map +1 -0
  113. package/dist/hooks/global/useLoginHealth.js +55 -0
  114. package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
  115. package/dist/hooks/group/useCreateGroup.js +75 -6
  116. package/dist/hooks/init/useChatBootPhase.d.ts +2 -0
  117. package/dist/hooks/init/useChatBootPhase.d.ts.map +1 -1
  118. package/dist/hooks/init/useChatBootPhase.js +11 -6
  119. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  120. package/dist/hooks/message/useMessage.js +97 -28
  121. package/dist/hooks/message/usePinnedMessages.d.ts.map +1 -1
  122. package/dist/hooks/message/usePinnedMessages.js +13 -2
  123. package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -1
  124. package/dist/hooks/message/useRevokeMessage.js +25 -0
  125. package/dist/hooks/message/useSendMessage.d.ts +15 -12
  126. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  127. package/dist/hooks/message/useSendMessage.js +199 -133
  128. package/dist/hooks/search/useSearchMessage.d.ts.map +1 -1
  129. package/dist/hooks/search/useSearchMessage.js +57 -3
  130. package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
  131. package/dist/hooks/session/useConversationSessionState.js +4 -1
  132. package/dist/hooks/session/useGetSession.d.ts.map +1 -1
  133. package/dist/hooks/session/useGetSession.js +2 -0
  134. package/dist/hooks/session/useGetSessionList.d.ts +235 -0
  135. package/dist/hooks/session/useGetSessionList.d.ts.map +1 -0
  136. package/dist/hooks/session/useGetSessionList.js +23 -0
  137. package/dist/hooks/session/useSessionLatestPreview.d.ts.map +1 -1
  138. package/dist/hooks/session/useSessionLatestPreview.js +34 -4
  139. package/dist/hooks/user/useAuth.d.ts +1 -1
  140. package/dist/hooks/user/useAuth.d.ts.map +1 -1
  141. package/dist/hooks/user/useAuth.js +31 -15
  142. package/dist/index.d.ts +1 -0
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +1 -0
  145. package/dist/locales/vi/boot.json +5 -1
  146. package/dist/locales/vi/common.json +35 -15
  147. package/dist/locales/vi/conversation-inbox.json +2 -1
  148. package/dist/screens/conversation-detail/index.d.ts.map +1 -1
  149. package/dist/screens/conversation-detail/index.js +6 -0
  150. package/dist/screens/desk-message/DeskBootCard.d.ts.map +1 -1
  151. package/dist/screens/desk-message/DeskBootCard.js +6 -4
  152. package/dist/store/boot.d.ts +5 -0
  153. package/dist/store/boot.d.ts.map +1 -1
  154. package/dist/store/boot.js +11 -1
  155. package/dist/store/conversation.d.ts.map +1 -1
  156. package/dist/store/conversation.js +5 -1
  157. package/dist/store/session.d.ts +1 -13
  158. package/dist/store/session.d.ts.map +1 -1
  159. package/dist/store/session.js +10 -9
  160. package/dist/store/sessionFolder.d.ts +2 -0
  161. package/dist/store/sessionFolder.d.ts.map +1 -1
  162. package/dist/store/sessionFolder.js +3 -1
  163. package/dist/store/type.d.ts +7 -1
  164. package/dist/store/type.d.ts.map +1 -1
  165. package/dist/styles/global.css +1 -1
  166. package/dist/types/chat.d.ts +1 -1
  167. package/dist/types/chat.d.ts.map +1 -1
  168. package/dist/utils/common.d.ts.map +1 -1
  169. package/dist/utils/common.js +4 -1
  170. package/dist/utils/download.d.ts +10 -0
  171. package/dist/utils/download.d.ts.map +1 -1
  172. package/dist/utils/download.js +51 -0
  173. package/dist/utils/fileValidation.d.ts +7 -0
  174. package/dist/utils/fileValidation.d.ts.map +1 -1
  175. package/dist/utils/fileValidation.js +37 -13
  176. package/dist/utils/latestMessagePreviewable.d.ts +2 -0
  177. package/dist/utils/latestMessagePreviewable.d.ts.map +1 -0
  178. package/dist/utils/latestMessagePreviewable.js +18 -0
  179. package/dist/utils/mentionMembers.d.ts +3 -0
  180. package/dist/utils/mentionMembers.d.ts.map +1 -0
  181. package/dist/utils/mentionMembers.js +29 -0
  182. package/dist/utils/messageLog.d.ts +2 -2
  183. package/dist/utils/messageLog.d.ts.map +1 -1
  184. package/dist/utils/messageLog.js +129 -40
  185. package/dist/utils/notifiableMessage.d.ts +7 -0
  186. package/dist/utils/notifiableMessage.d.ts.map +1 -0
  187. package/dist/utils/notifiableMessage.js +26 -0
  188. package/dist/utils/withSdkTimeout.d.ts +11 -0
  189. package/dist/utils/withSdkTimeout.d.ts.map +1 -0
  190. package/dist/utils/withSdkTimeout.js +30 -0
  191. package/package.json +1 -1
@@ -25,7 +25,7 @@ import CannedResponse from "../../../components/canned-response";
25
25
  import CannedResponsePanel from "../../../components/canned-response/CannedResponsePanel";
26
26
  import CrmComposerCard from "./CrmComposerCard";
27
27
  import { CannedResponseTriggerPlugin } from "./CannedResponsePlugin";
28
- import { useBoolean } from "ahooks";
28
+ import { useBoolean, useLatest } from "ahooks";
29
29
  import useAuthStore from "../../../store/auth";
30
30
  import useConversationStore from "../../../store/conversation";
31
31
  import { useCurrentUserAccountType } from "../../../hooks/user/useCurrentUserAccountType";
@@ -142,6 +142,10 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
142
142
  };
143
143
  }, [showLinkPreview, previewUrl, previewMetadata]);
144
144
  const [isSessionClosedModalOpen, { setTrue: openSessionClosedModal, setFalse: closeSessionClosedModal },] = useBoolean(false);
145
+ // Conversations whose message is still being built. Keyed per thread so a send in
146
+ // one thread neither blocks nor clears the composer of the thread opened next.
147
+ const buildingConversationsRef = useRef(new Set());
148
+ const currentConversationIdRef = useLatest(conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID);
145
149
  const pendingMessageRef = useRef(null);
146
150
  const { mutateAsync: createSession, isPending: isCreatingSession } = useCreateSession();
147
151
  const { mutateAsync: reopenSession, isPending: isReopeningSession } = useReopenSession();
@@ -163,6 +167,7 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
163
167
  ? t("enter_internal_message")
164
168
  : t("enter_message");
165
169
  const onSendMessage = useCallback(({ plainText, richText, type, isInternal, mentions, }) => {
170
+ var _a;
166
171
  if ((currentSession === null || currentSession === void 0 ? void 0 : currentSession.isClosed) && !isInternal) {
167
172
  pendingMessageRef.current = {
168
173
  plainText,
@@ -172,34 +177,61 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
172
177
  files: [...listUploadFiles],
173
178
  urlMetadata: resolveUrlMetadata(),
174
179
  mentions,
180
+ conversationId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID,
175
181
  };
176
182
  openSessionClosedModal();
177
183
  return false;
178
184
  }
179
- sendMergeMessage({
180
- plainText,
181
- richText,
182
- files: listUploadFiles,
183
- currentSession,
184
- isInternal,
185
- urlMetadata: resolveUrlMetadata(),
186
- mentions,
185
+ // A second Enter while the first message is still being built would send it twice.
186
+ const conversationId = (_a = conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) !== null && _a !== void 0 ? _a : "";
187
+ if (buildingConversationsRef.current.has(conversationId))
188
+ return false;
189
+ buildingConversationsRef.current.add(conversationId);
190
+ const sentFileUids = new Set(listUploadFiles.map((file) => file.uid));
191
+ // The composer is cleared by the caller only when this resolves true, so a failed
192
+ // build (hung or rejected core call) keeps the text and files for another try.
193
+ return new Promise((resolve) => {
194
+ const settle = (shown) => {
195
+ buildingConversationsRef.current.delete(conversationId);
196
+ if (!shown)
197
+ toastMessage.error(t("message_send_failed_no_retry"));
198
+ if (shown) {
199
+ // The upload list is composer-wide: even after a thread switch the sent files
200
+ // must go, or they would ride along with the next thread's message.
201
+ setListUploadFiles((files) => files.filter((file) => !sentFileUids.has(file.uid)));
202
+ }
203
+ // After a thread switch the composer may still hold the sent text; the caller
204
+ // clears it only if it is unchanged, so a different draft is never touched.
205
+ resolve(shown);
206
+ };
207
+ void sendMergeMessage({
208
+ plainText,
209
+ richText,
210
+ files: listUploadFiles,
211
+ currentSession,
212
+ isInternal,
213
+ urlMetadata: resolveUrlMetadata(),
214
+ mentions,
215
+ onBuilt: settle,
216
+ });
187
217
  });
188
- setListUploadFiles([]);
189
- return true;
190
218
  }, [
219
+ t,
191
220
  sendMergeMessage,
192
221
  listUploadFiles,
193
222
  currentSession,
194
223
  openSessionClosedModal,
195
224
  resolveUrlMetadata,
225
+ conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID,
226
+ currentConversationIdRef,
196
227
  ]);
197
228
  const executePendingSend = useCallback((newSession) => {
198
229
  const pending = pendingMessageRef.current;
199
230
  if (!pending)
200
231
  return;
201
232
  const sessionToUse = newSession || currentSession;
202
- sendMergeMessage({
233
+ pendingMessageRef.current = null;
234
+ void sendMergeMessage({
203
235
  plainText: pending.plainText,
204
236
  richText: pending.richText,
205
237
  files: pending.files,
@@ -207,11 +239,20 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
207
239
  isInternal: pending.isInternal,
208
240
  urlMetadata: pending.urlMetadata,
209
241
  mentions: pending.mentions,
242
+ onBuilt: (shown) => {
243
+ if (!shown) {
244
+ toastMessage.error(t("message_send_failed_no_retry"));
245
+ return;
246
+ }
247
+ const sentUids = new Set(pending.files.map((file) => file.uid));
248
+ setListUploadFiles((files) => files.filter((file) => !sentUids.has(file.uid)));
249
+ // Only the thread the message was written in has a text composer to clear.
250
+ if (currentConversationIdRef.current !== pending.conversationId)
251
+ return;
252
+ emit("CLEAR_COMPOSER");
253
+ },
210
254
  });
211
- setListUploadFiles([]);
212
- pendingMessageRef.current = null;
213
- emit("CLEAR_COMPOSER");
214
- }, [sendMergeMessage, currentSession]);
255
+ }, [t, sendMergeMessage, currentSession, currentConversationIdRef]);
215
256
  const handleSessionAction = useCallback(async (action) => {
216
257
  const conversationId = conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID;
217
258
  if (!conversationId)
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAoB7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EA2Df,CAAC"}
1
+ {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAoB7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EAwDf,CAAC"}
@@ -2,7 +2,6 @@ import { useEffect, useMemo } from "react";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import { useGroupMembers } from "../../../../hooks/group/useGroupMembers";
4
4
  import { useSearchGroupMembers } from "../../../../hooks/group/useSearchGroupMembers";
5
- import useAuthStore from "../../../../store/auth";
6
5
  export const ALL_MEMBERS_USER_ID = "__all__";
7
6
  // Typing a name loads a big group's remaining members up to this many.
8
7
  const MENTION_MEMBER_LOAD_LIMIT = 10000;
@@ -27,7 +26,6 @@ export const useMentionOptions = (groupID, query) => {
27
26
  // Core keeps only part of a big group locally, so its search alone misses
28
27
  // members; typing a name also loads the rest, merged in as pages arrive.
29
28
  const { results: searchResults } = useSearchGroupMembers(groupID, hasMore ? query : "");
30
- const currentUserID = useAuthStore((state) => state.userID);
31
29
  const hasKeyword = query.trim().length > 0;
32
30
  useEffect(() => {
33
31
  if (!hasKeyword || !hasMore || isLoadingMore)
@@ -42,10 +40,8 @@ export const useMentionOptions = (groupID, query) => {
42
40
  const candidates = keyword
43
41
  ? [...members, ...searchResults.filter((member) => !loadedIDs.has(member.userID))]
44
42
  : members;
45
- // Self-tagging isn't a useful mention target — the sender already
46
- // knows who they are — so the current user is filtered out entirely.
43
+ // The sender stays in the list: the spec lists every group member.
47
44
  const memberOptions = candidates
48
- .filter((member) => member.userID !== currentUserID)
49
45
  .map((member) => ({
50
46
  userID: member.userID,
51
47
  displayName: member.nickname || member.userID,
@@ -69,5 +65,5 @@ export const useMentionOptions = (groupID, query) => {
69
65
  };
70
66
  const matchesAll = !keyword || allOption.displayName.toLowerCase().includes(keyword);
71
67
  return matchesAll ? [allOption, ...memberOptions] : memberOptions;
72
- }, [members, searchResults, query, t, currentUserID]);
68
+ }, [members, searchResults, query, t]);
73
69
  };
@@ -0,0 +1,7 @@
1
+ import { LexicalEditor } from "lexical";
2
+ export declare const resetEditorIfUnchanged: (editor: LexicalEditor, sent: {
3
+ plainText: string;
4
+ richText: string;
5
+ }) => void;
6
+ export declare const resetEditorAfterSend: (editor: LexicalEditor) => void;
7
+ //# sourceMappingURL=resetEditorAfterSend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resetEditorAfterSend.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/resetEditorAfterSend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAoE,MAAM,SAAS,CAAC;AAW1G,eAAO,MAAM,sBAAsB,GACjC,QAAQ,aAAa,EACrB,MAAM;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,SAU9C,CAAC;AAIF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,aAAa,SAgDzD,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { $generateHtmlFromNodes } from "@lexical/html";
2
+ import { $createParagraphNode, $getRoot, $getSelection, $isRangeSelection } from "lexical";
3
+ import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
4
+ import { $isQuoteNode } from "@lexical/rich-text";
5
+ import { clearComposerHistoryAfterUpdate } from "./clearComposerHistory";
6
+ // Resets only when the composer still holds what was sent; text typed while the
7
+ // message was being built stays as the next draft.
8
+ export const resetEditorIfUnchanged = (editor, sent) => {
9
+ let plainText = "";
10
+ let richText = "";
11
+ editor.getEditorState().read(() => {
12
+ plainText = $getRoot().getTextContent();
13
+ richText = $generateHtmlFromNodes(editor);
14
+ });
15
+ if (plainText !== sent.plainText || richText !== sent.richText)
16
+ return;
17
+ resetEditorAfterSend(editor);
18
+ };
19
+ // Empties the composer after a send but keeps the typing format, list and quote
20
+ // context, so the next message continues in the same block style.
21
+ export const resetEditorAfterSend = (editor) => {
22
+ clearComposerHistoryAfterUpdate(editor);
23
+ editor.update(() => {
24
+ const root = $getRoot();
25
+ root.clear();
26
+ const paragraph = $createParagraphNode();
27
+ root.append(paragraph);
28
+ paragraph.select();
29
+ const selection = $getSelection();
30
+ if ($isRangeSelection(selection)) {
31
+ const anchorNode = selection.anchor.getNode();
32
+ const topLevelNode = anchorNode.getTopLevelElementOrThrow();
33
+ const isListNode = $isListNode(topLevelNode);
34
+ const isQuoteNode = $isQuoteNode(topLevelNode);
35
+ const listType = isListNode
36
+ ? topLevelNode.getListType()
37
+ : undefined;
38
+ if (selection.hasFormat("bold")) {
39
+ selection.formatText("bold");
40
+ }
41
+ if (selection.hasFormat("italic")) {
42
+ selection.formatText("italic");
43
+ }
44
+ if (selection.hasFormat("strikethrough")) {
45
+ selection.formatText("strikethrough");
46
+ }
47
+ if (isQuoteNode) {
48
+ // Nếu đang là QuoteNode → chuyển về Paragraph (bình thường)
49
+ const paragraph = $createParagraphNode();
50
+ // copy con của quote sang paragraph
51
+ const children = topLevelNode.getChildren();
52
+ children.forEach((child) => paragraph.append(child));
53
+ topLevelNode.replace(paragraph);
54
+ }
55
+ if (isListNode && listType) {
56
+ editor.dispatchCommand(listType === "number"
57
+ ? INSERT_ORDERED_LIST_COMMAND
58
+ : INSERT_UNORDERED_LIST_COMMAND, undefined);
59
+ }
60
+ }
61
+ });
62
+ };
@@ -14,7 +14,6 @@ export interface CrmMemberMenuHandlers {
14
14
  }
15
15
  interface BuildCrmMemberMenuParams {
16
16
  member: MemberDisplayItem;
17
- isRealGroup: boolean;
18
17
  permissions: GroupPermissions;
19
18
  actingIsOwner: boolean;
20
19
  /** Set when leaving is not allowed; shown under the item instead of failing on click. */
@@ -24,9 +23,10 @@ interface BuildCrmMemberMenuParams {
24
23
  }
25
24
  type MenuItems = NonNullable<MenuProps["items"]>;
26
25
  /**
27
- * CRM member menu: no always-disabled entries, a short reason under every
28
- * entry that is disabled for lack of rights, owner transfer and removal set apart.
26
+ * CRM member menu: actions the viewer has no right to are left out instead of
27
+ * shown disabled. Only leave keeps a reason, since it depends on live state.
28
+ * Empty result means the "..." button is hidden.
29
29
  */
30
- export declare const buildCrmMemberMenu: ({ member, isRealGroup, permissions, actingIsOwner, leaveBlockedReason, handlers, t, }: BuildCrmMemberMenuParams) => MenuItems;
30
+ export declare const buildCrmMemberMenu: ({ member, permissions, actingIsOwner, leaveBlockedReason, handlers, t, }: BuildCrmMemberMenuParams) => MenuItems;
31
31
  export {};
32
32
  //# sourceMappingURL=groupMemberMenuCrm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"groupMemberMenuCrm.d.ts","sourceRoot":"","sources":["../../../src/components/message/groupMemberMenuCrm.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC/C,aAAa,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,aAAa,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,CAAC,EAAE,SAAS,CAAC;CACd;AAED,KAAK,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAWjD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,uFAQhC,wBAAwB,KAAG,SAsF7B,CAAC"}
1
+ {"version":3,"file":"groupMemberMenuCrm.d.ts","sourceRoot":"","sources":["../../../src/components/message/groupMemberMenuCrm.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC/C,aAAa,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,aAAa,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,CAAC,EAAE,SAAS,CAAC;CACd;AAED,KAAK,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAWjD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,0EAOhC,wBAAwB,KAAG,SA4F7B,CAAC"}
@@ -3,11 +3,11 @@ import { CrmIcon as Icon } from "../../components/group/crmIcon";
3
3
  const label = (title, hint, tone) => (_jsxs("span", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: tone, children: title }), hint && _jsx("span", { className: "text-xs font-normal text-gray-600", children: hint })] }));
4
4
  const DIVIDER = { type: "divider" };
5
5
  /**
6
- * CRM member menu: no always-disabled entries, a short reason under every
7
- * entry that is disabled for lack of rights, owner transfer and removal set apart.
6
+ * CRM member menu: actions the viewer has no right to are left out instead of
7
+ * shown disabled. Only leave keeps a reason, since it depends on live state.
8
+ * Empty result means the "..." button is hidden.
8
9
  */
9
- export const buildCrmMemberMenu = ({ member, isRealGroup, permissions, actingIsOwner, leaveBlockedReason, handlers, t, }) => {
10
- const ownerOnly = actingIsOwner ? undefined : t("group_owner_only");
10
+ export const buildCrmMemberMenu = ({ member, permissions, actingIsOwner, leaveBlockedReason, handlers, t, }) => {
11
11
  if (member.isCurrentUser) {
12
12
  return [
13
13
  {
@@ -20,64 +20,77 @@ export const buildCrmMemberMenu = ({ member, isRealGroup, permissions, actingIsO
20
20
  },
21
21
  ];
22
22
  }
23
- const removeReason = !permissions.removeMember
24
- ? t("need_remove_member_permission")
25
- : member.isOwner
26
- ? t("cannot_remove_group_owner")
27
- : member.isAdmin && !actingIsOwner
28
- ? t("group_owner_only")
29
- : undefined;
23
+ // Mute and remove share one target guard: never the owner, and another admin only for the owner.
24
+ const canManageTarget = permissions.removeMember && !member.isOwner && (!member.isAdmin || actingIsOwner);
30
25
  const adminItems = member.isAdmin
31
26
  ? [
32
- {
33
- key: "configure-admin-permission",
34
- label: label(t("admin_permission_settings"), permissions.transferOwnership ? undefined : ownerOnly),
35
- icon: _jsx(Icon, { icon: "shield-o", size: 16 }),
36
- disabled: !permissions.transferOwnership,
37
- onClick: () => handlers.onConfigureAdmin(member),
38
- },
39
- {
40
- key: "remove-admin",
41
- label: label(t("remove_from_admin"), permissions.addAdmin ? undefined : ownerOnly),
42
- icon: _jsx(Icon, { icon: "shield-fail-o", size: 16 }),
43
- disabled: !permissions.addAdmin,
44
- onClick: () => handlers.onRemoveAdmin(member),
45
- },
27
+ ...(permissions.transferOwnership
28
+ ? [
29
+ {
30
+ key: "configure-admin-permission",
31
+ label: label(t("admin_permission_settings")),
32
+ icon: _jsx(Icon, { icon: "shield-o", size: 16 }),
33
+ onClick: () => handlers.onConfigureAdmin(member),
34
+ },
35
+ ]
36
+ : []),
37
+ ...(permissions.addAdmin
38
+ ? [
39
+ {
40
+ key: "remove-admin",
41
+ label: label(t("remove_from_admin")),
42
+ icon: _jsx(Icon, { icon: "shield-fail-o", size: 16 }),
43
+ onClick: () => handlers.onRemoveAdmin(member),
44
+ },
45
+ ]
46
+ : []),
46
47
  ]
47
- : [
48
- {
49
- key: "assign-admin",
50
- label: label(t("assign_as_admin"), permissions.addAdmin ? undefined : ownerOnly),
51
- icon: _jsx(Icon, { icon: "shield-o", size: 16 }),
52
- disabled: !permissions.addAdmin,
53
- onClick: () => handlers.onAskAssignAdmin(member),
54
- },
55
- ];
56
- return [
57
- {
58
- key: "mute",
59
- label: label(member.isSendMessageDisabled ? t("unmute_member") : t("mute_member"), permissions.removeMember ? undefined : t("need_remove_member_permission")),
60
- icon: _jsx(Icon, { icon: "bell-off-o", size: 16 }),
61
- disabled: !permissions.removeMember,
62
- onClick: () => member.isSendMessageDisabled ? handlers.onToggleMute(member) : handlers.onAskMute(member),
63
- },
64
- ...adminItems,
65
- DIVIDER,
66
- {
67
- key: "transfer-owner",
68
- label: label(t("transfer_owner"), permissions.transferOwnership ? t("transfer_owner_hint") : ownerOnly, permissions.transferOwnership ? "text-warning-800" : undefined),
69
- icon: _jsx(Icon, { icon: "key-o", size: 16, className: permissions.transferOwnership ? "text-warning-800" : undefined }),
70
- disabled: !permissions.transferOwnership,
71
- onClick: () => handlers.onAskTransfer(member),
72
- },
73
- DIVIDER,
74
- {
75
- key: "remove-member",
76
- label: label(t("remove_from_group"), removeReason),
77
- icon: _jsx(Icon, { icon: "user-x", size: 16 }),
78
- danger: true,
79
- disabled: Boolean(removeReason),
80
- onClick: () => handlers.onAskRemove(member),
81
- },
48
+ : permissions.addAdmin
49
+ ? [
50
+ {
51
+ key: "assign-admin",
52
+ label: label(t("assign_as_admin")),
53
+ icon: _jsx(Icon, { icon: "shield-o", size: 16 }),
54
+ onClick: () => handlers.onAskAssignAdmin(member),
55
+ },
56
+ ]
57
+ : [];
58
+ const groups = [
59
+ canManageTarget
60
+ ? [
61
+ {
62
+ key: "mute",
63
+ label: label(member.isSendMessageDisabled ? t("unmute_member") : t("mute_member")),
64
+ icon: _jsx(Icon, { icon: "bell-off-o", size: 16 }),
65
+ onClick: () => member.isSendMessageDisabled ? handlers.onToggleMute(member) : handlers.onAskMute(member),
66
+ },
67
+ ]
68
+ : [],
69
+ adminItems,
70
+ permissions.transferOwnership
71
+ ? [
72
+ {
73
+ key: "transfer-owner",
74
+ label: label(t("transfer_owner"), t("transfer_owner_hint"), "text-warning-800"),
75
+ icon: _jsx(Icon, { icon: "key-o", size: 16, className: "text-warning-800" }),
76
+ onClick: () => handlers.onAskTransfer(member),
77
+ },
78
+ ]
79
+ : [],
80
+ canManageTarget
81
+ ? [
82
+ {
83
+ key: "remove-member",
84
+ label: label(t("remove_from_group")),
85
+ icon: _jsx(Icon, { icon: "user-x", size: 16 }),
86
+ danger: true,
87
+ onClick: () => handlers.onAskRemove(member),
88
+ },
89
+ ]
90
+ : [],
82
91
  ];
92
+ // Dividers only between non-empty groups so the menu never starts, ends or doubles up on one.
93
+ return groups
94
+ .filter((group) => group.length > 0)
95
+ .flatMap((group, index) => (index === 0 ? group : [DIVIDER, ...group]));
83
96
  };
@@ -4,7 +4,7 @@ import { Spin } from "antd";
4
4
  import { Icon } from "../../../components/icon";
5
5
  import { documentIcon } from "../../../assets/svg";
6
6
  import { renderFileSize, shortenFileName } from "../../../utils/common";
7
- import { downloadFileFromUrl } from "../../../utils/download";
7
+ import { downloadFileFromUrl, openFileOrDownload } from "../../../utils/download";
8
8
  const FileMessageItem = (props) => {
9
9
  var _a;
10
10
  const { message } = props;
@@ -17,7 +17,7 @@ const FileMessageItem = (props) => {
17
17
  const handleView = () => {
18
18
  if (!hasUrl)
19
19
  return;
20
- window.open(sourceUrl, "_blank", "noopener,noreferrer");
20
+ void openFileOrDownload(sourceUrl, fileName);
21
21
  };
22
22
  const handleDownload = () => {
23
23
  downloadFileFromUrl(sourceUrl, fileName);
@@ -1 +1 @@
1
- {"version":3,"file":"SystemLogMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/SystemLogMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAS3D,UAAU,yBAAyB;IACjC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,oBAAoB,GAAI,aAAa,yBAAyB,mDA0CnE,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"SystemLogMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/SystemLogMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAU3D,UAAU,yBAAyB;IACjC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,oBAAoB,GAAI,aAAa,yBAAyB,mDA4CnE,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -2,12 +2,15 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { getSystemLogMessageHtml, getSystemLogMessageText, } from "../../../utils/messageLog";
4
4
  import { sanitizeHtml } from "../../../utils/common";
5
+ import { useTranslation } from "react-i18next";
5
6
  import useAuthStore from "../../../store/auth";
6
7
  import CrmSystemLine from "./CrmSystemLine";
7
8
  const SystemLogMessageItem = ({ message }) => {
8
9
  const isCrm = useAuthStore((state) => state.isCrm);
9
- const html = getSystemLogMessageHtml(message);
10
- const text = getSystemLogMessageText(message);
10
+ const viewerId = useAuthStore((state) => state.userID);
11
+ const { t } = useTranslation();
12
+ const html = getSystemLogMessageHtml(message, viewerId, t);
13
+ const text = getSystemLogMessageText(message, viewerId, t);
11
14
  if (!html && !text) {
12
15
  return null;
13
16
  }
@@ -15,7 +15,7 @@ const SearchConversationAsGroups = (props) => {
15
15
  scope,
16
16
  });
17
17
  if ((groups.length === 0 && !isLoading) || searchTerm.trim() === "")
18
- return _jsx(Empty, { description: t("no_conversation") });
18
+ return _jsx(Empty, { description: t("search_no_result") });
19
19
  if (isLoading)
20
20
  return (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Spin, {}) }));
21
21
  return (_jsx("div", { id: "scrollableSearchGroupsTab", className: "h-full overflow-auto", "data-cp-name": "SearchConversationAsGroups", children: _jsx(InfiniteScroll, { dataLength: groups.length, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableSearchGroupsTab", children: groups.map((item) => (_jsx(SearchItemAsGroup, { group: item, searchTerm: searchTerm }, item.conversationId))) }) }));
@@ -18,7 +18,7 @@ const SearchConversationAsMessages = (props) => {
18
18
  options: { scope },
19
19
  });
20
20
  if ((dataFlatten.length === 0 && !isLoading) || searchTerm.trim() === "")
21
- return _jsx(Empty, { description: t("no_conversation") });
21
+ return _jsx(Empty, { description: t("search_no_result") });
22
22
  if (isLoading)
23
23
  return (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Spin, {}) }));
24
24
  return (_jsx("div", { id: "scrollableSearchMessagesTab", className: "h-full overflow-auto", "data-cp-name": "SearchConversationAsMessages", children: _jsx(InfiniteScroll, { dataLength: dataFlatten.length, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableSearchMessagesTab", children: dataFlatten.map((item) => (_jsx(SearchItemAsMessage, { message: item.chatLog, searchTerm: searchTerm, displayTag: item.displayTag, tag: item.tag }, item.chatLog.clientMsgID))) }) }));
@@ -16,7 +16,7 @@ const SearchConversationMyInbox = ({ searchTerm = "", }) => {
16
16
  options: { untilEmptyPage: true },
17
17
  });
18
18
  if ((dataFlatten.length === 0 && !isLoading) || searchTerm.trim() === "")
19
- return _jsx(Empty, { description: t("no_conversation") });
19
+ return _jsx(Empty, { description: t("search_no_result") });
20
20
  if (isLoading)
21
21
  return (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Spin, {}) }));
22
22
  return (_jsxs("div", { className: "overflow-auto", "data-cp-name": "SearchConversationMyInbox", children: [dataFlatten.map((item) => (_jsx(SearchItemAsMessage, { message: item.chatLog, searchTerm: searchTerm, displayTag: item.displayTag }, item.chatLog.clientMsgID))), _jsx("div", { className: "flex justify-center py-2", children: _jsx(Button, { type: "link", loading: isFetchingNextPage, onClick: () => fetchNextPage(), children: t("see_more") }) })] }));
@@ -14,9 +14,10 @@ interface SessionFolderListProps {
14
14
  };
15
15
  }
16
16
  /**
17
- * Folders of one scope. The "open" parent has two click targets: the label
18
- * only reveals its children, the chevron collapses them. The API cannot list
19
- * all open sessions at once, so the label never changes the filter.
17
+ * Folders of one scope. In "my messages" the "open" parent has two click
18
+ * targets: the label selects every open session (and reveals the children), the
19
+ * chevron collapses them. A team's parent label only reveals its children: the
20
+ * team query has no "all open" list.
20
21
  */
21
22
  declare const SessionFolderList: ({ summary, teamId, activeFolderId, onFilterSelect, allGroups, }: SessionFolderListProps) => import("react/jsx-runtime").JSX.Element;
22
23
  export default SessionFolderList;
@@ -1 +1 @@
1
- {"version":3,"file":"SessionFolderList.d.ts","sourceRoot":"","sources":["../../../src/components/session/SessionFolderList.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAQ3D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,gEAAgE;IAChE,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CACvD;AAED;;;;GAIG;AACH,QAAA,MAAM,iBAAiB,GAAI,iEAMxB,sBAAsB,4CAyFxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"SessionFolderList.d.ts","sourceRoot":"","sources":["../../../src/components/session/SessionFolderList.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAQ3D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,gEAAgE;IAChE,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CACvD;AAED;;;;;GAKG;AACH,QAAA,MAAM,iBAAiB,GAAI,iEAMxB,sBAAsB,4CAoGxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -4,13 +4,15 @@ import { useState } from "react";
4
4
  import { useTranslation } from "react-i18next";
5
5
  import { Tooltip } from "antd";
6
6
  import { StrokeIcon } from "../../components/icon/StrokeIcon";
7
- import { buildFolderId } from "../../store/sessionFolder";
7
+ import { buildFolderId, OPEN_FOLDER_KEY } from "../../store/sessionFolder";
8
+ import clsx from "clsx";
8
9
  import FolderRow, { FolderCount } from "./FolderRow";
9
10
  import { getCompletedFilterConfig, getFilterCount, getOpenFilterConfigs, } from "./sessionFilters";
10
11
  /**
11
- * Folders of one scope. The "open" parent has two click targets: the label
12
- * only reveals its children, the chevron collapses them. The API cannot list
13
- * all open sessions at once, so the label never changes the filter.
12
+ * Folders of one scope. In "my messages" the "open" parent has two click
13
+ * targets: the label selects every open session (and reveals the children), the
14
+ * chevron collapses them. A team's parent label only reveals its children: the
15
+ * team query has no "all open" list.
14
16
  */
15
17
  const SessionFolderList = ({ summary, teamId, activeFolderId, onFilterSelect, allGroups, }) => {
16
18
  const { t } = useTranslation();
@@ -19,9 +21,16 @@ const SessionFolderList = ({ summary, teamId, activeFolderId, onFilterSelect, al
19
21
  const completed = getCompletedFilterConfig();
20
22
  const isActive = (filterKey) => activeFolderId ===
21
23
  buildFolderId({ scope: isTeam ? "team" : "mine", teamId, filterKey });
24
+ const isOpenActive = !isTeam && isActive(OPEN_FOLDER_KEY);
22
25
  const groupLabel = t("active_sessions");
23
26
  const toggleLabel = t(isOpenGroupExpanded ? "folder_group_collapse" : "folder_group_expand", { name: groupLabel });
24
- return (_jsxs("div", { className: "flex flex-col gap-0.5", "data-cp-name": "SessionFolderList", children: [_jsxs("div", { className: "flex items-center gap-0.5", children: [_jsxs("button", { type: "button", onClick: () => setIsOpenGroupExpanded(true), className: `flex h-9 min-w-0 flex-1 items-center gap-2.5 rounded-lg pr-2 text-left text-sm text-gray-900 hover:bg-gray-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-400 ${isTeam ? "pl-5" : "pl-2"}`, children: [_jsx(StrokeIcon, { name: "message-circle", size: 18, className: "text-gray-600" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: groupLabel }), _jsx(FolderCount, { count: summary.activeSessionCount })] }), !isTeam && (_jsx(Tooltip, { title: toggleLabel, children: _jsx("button", { type: "button", "aria-label": toggleLabel, "aria-expanded": isOpenGroupExpanded, onClick: () => setIsOpenGroupExpanded((expanded) => !expanded), className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-400", children: _jsx(StrokeIcon, { name: isOpenGroupExpanded ? "chevron-down" : "chevron-right", size: 16 }) }) }))] }), (isTeam || isOpenGroupExpanded) &&
27
+ return (_jsxs("div", { className: "flex flex-col gap-0.5", "data-cp-name": "SessionFolderList", children: [_jsxs("div", { className: "flex items-center gap-0.5", children: [_jsxs("button", { type: "button", onClick: () => {
28
+ setIsOpenGroupExpanded(true);
29
+ if (!isTeam)
30
+ onFilterSelect({ status: undefined, tag: undefined, open: true });
31
+ }, "aria-current": isOpenActive ? "true" : undefined, className: clsx("flex h-9 min-w-0 flex-1 items-center gap-2.5 rounded-lg pr-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-400", isTeam ? "pl-5" : "pl-2", isOpenActive
32
+ ? "bg-primary-25 font-semibold text-primary-500"
33
+ : "text-gray-900 hover:bg-gray-5"), children: [_jsx(StrokeIcon, { name: "message-circle", size: 18, className: "text-gray-600" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: groupLabel }), _jsx(FolderCount, { count: summary.activeSessionCount })] }), !isTeam && (_jsx(Tooltip, { title: toggleLabel, children: _jsx("button", { type: "button", "aria-label": toggleLabel, "aria-expanded": isOpenGroupExpanded, onClick: () => setIsOpenGroupExpanded((expanded) => !expanded), className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-400", children: _jsx(StrokeIcon, { name: isOpenGroupExpanded ? "chevron-down" : "chevron-right", size: 16 }) }) }))] }), (isTeam || isOpenGroupExpanded) &&
25
34
  getOpenFilterConfigs(isTeam).map((config) => (_jsx("div", { className: "flex", children: _jsx(FolderRow, { label: t(config.labelKey), iconName: config.iconName, iconClassName: config.iconClassName, count: getFilterCount(config, summary), highlighted: config.highlighted, active: isActive(config.key), indentClassName: isTeam ? "pl-8" : "pl-7", onClick: () => onFilterSelect(Object.assign(Object.assign({}, config.filter), (teamId && { teamId }))) }) }, config.key))), completed && (_jsx("div", { className: "flex", children: _jsx(FolderRow, { label: t(completed.labelKey), iconName: completed.iconName, iconClassName: completed.iconClassName, count: getFilterCount(completed, summary), active: isActive(completed.key), indentClassName: isTeam ? "pl-5" : "pl-2", onClick: () => onFilterSelect(Object.assign(Object.assign({}, completed.filter), (teamId && { teamId }))) }) })), allGroups && (_jsx("div", { className: "flex", children: _jsx(FolderRow, { label: t("all_groups"), iconName: "inbox", iconClassName: "text-gray-600", active: allGroups.active, onClick: allGroups.onSelect }) }))] }));
26
35
  };
27
36
  export default SessionFolderList;
@@ -1 +1 @@
1
- {"version":3,"file":"GroupInfoBodyCrm.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupInfoBodyCrm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAUzC,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,aAAa,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAClD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACrD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACtD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC/D,oBAAoB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,mBAAmB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,SAAS,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC/C;AA6BD,kGAAkG;AAClG,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CA4JrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"GroupInfoBodyCrm.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupInfoBodyCrm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAUzC,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,aAAa,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAClD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACrD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACtD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC/D,oBAAoB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,mBAAmB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,SAAS,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC/C;AA6BD,kGAAkG;AAClG,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAwJrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}