@droppii-org/chat-sdk 0.1.5 → 0.1.7

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 (101) hide show
  1. package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -1
  2. package/dist/components/cannedResponse/CannedResponseBody.js +54 -3
  3. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  4. package/dist/components/conversation/ConversationBySessionItem.js +7 -2
  5. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  6. package/dist/components/conversation/DeskConversationList.js +22 -17
  7. package/dist/components/message/MessageHeader.d.ts +2 -2
  8. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  9. package/dist/components/message/MessageHeader.js +5 -2
  10. package/dist/components/message/MessageList.d.ts.map +1 -1
  11. package/dist/components/message/MessageList.js +31 -22
  12. package/dist/components/message/SelectSession.d.ts.map +1 -1
  13. package/dist/components/message/SelectSession.js +7 -9
  14. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  15. package/dist/components/message/footer/ActionBar.js +3 -2
  16. package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -1
  17. package/dist/components/message/footer/CannedResponsePlugin.js +37 -1
  18. package/dist/components/message/footer/ComposerEditor.d.ts +7 -0
  19. package/dist/components/message/footer/ComposerEditor.d.ts.map +1 -0
  20. package/dist/components/message/footer/ComposerEditor.js +13 -0
  21. package/dist/components/message/footer/ComposerTabs.d.ts +9 -0
  22. package/dist/components/message/footer/ComposerTabs.d.ts.map +1 -0
  23. package/dist/components/message/footer/ComposerTabs.js +37 -0
  24. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  25. package/dist/components/message/footer/EnterHandler.js +10 -1
  26. package/dist/components/message/footer/index.d.ts +2 -2
  27. package/dist/components/message/footer/index.d.ts.map +1 -1
  28. package/dist/components/message/footer/index.js +45 -9
  29. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
  30. package/dist/components/message/item/QuoteMessage.js +44 -35
  31. package/dist/components/message/item/index.d.ts.map +1 -1
  32. package/dist/components/message/item/index.js +11 -1
  33. package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
  34. package/dist/components/session/DeskAssignedSession.js +14 -109
  35. package/dist/components/session/DeskTeamInbox.d.ts +3 -0
  36. package/dist/components/session/DeskTeamInbox.d.ts.map +1 -0
  37. package/dist/components/session/DeskTeamInbox.js +56 -0
  38. package/dist/components/session/SessionFilterMenu.d.ts +13 -0
  39. package/dist/components/session/SessionFilterMenu.d.ts.map +1 -0
  40. package/dist/components/session/SessionFilterMenu.js +27 -0
  41. package/dist/components/session/sessionMenuItems.d.ts +26 -0
  42. package/dist/components/session/sessionMenuItems.d.ts.map +1 -0
  43. package/dist/components/session/sessionMenuItems.js +108 -0
  44. package/dist/hooks/conversation/useConversationPreview.d.ts +12 -0
  45. package/dist/hooks/conversation/useConversationPreview.d.ts.map +1 -0
  46. package/dist/hooks/conversation/useConversationPreview.js +22 -0
  47. package/dist/hooks/message/useConversationMessages.d.ts +27 -0
  48. package/dist/hooks/message/useConversationMessages.d.ts.map +1 -0
  49. package/dist/hooks/message/useConversationMessages.js +29 -0
  50. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  51. package/dist/hooks/message/usePullSessionMessages.d.ts +9 -0
  52. package/dist/hooks/message/usePullSessionMessages.d.ts.map +1 -0
  53. package/dist/hooks/message/usePullSessionMessages.js +27 -0
  54. package/dist/hooks/message/useSendMessage.d.ts +8 -6
  55. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  56. package/dist/hooks/message/useSendMessage.js +8 -8
  57. package/dist/hooks/session/useConversationSessionState.d.ts +21 -0
  58. package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -0
  59. package/dist/hooks/session/useConversationSessionState.js +41 -0
  60. package/dist/hooks/session/useGetSession.d.ts.map +1 -1
  61. package/dist/hooks/session/useGetSession.js +138 -52
  62. package/dist/hooks/session/useGetTeamSessionSummary.d.ts +3 -0
  63. package/dist/hooks/session/useGetTeamSessionSummary.d.ts.map +1 -0
  64. package/dist/hooks/session/useGetTeamSessionSummary.js +12 -0
  65. package/dist/hooks/session/useIsJoinedGroup.d.ts +5 -0
  66. package/dist/hooks/session/useIsJoinedGroup.d.ts.map +1 -0
  67. package/dist/hooks/session/useIsJoinedGroup.js +24 -0
  68. package/dist/hooks/session/useJoinGroupFlow.d.ts +12 -0
  69. package/dist/hooks/session/useJoinGroupFlow.d.ts.map +1 -0
  70. package/dist/hooks/session/useJoinGroupFlow.js +59 -0
  71. package/dist/hooks/session/useJoinSession.d.ts +3 -0
  72. package/dist/hooks/session/useJoinSession.d.ts.map +1 -0
  73. package/dist/hooks/session/useJoinSession.js +38 -0
  74. package/dist/hooks/user/useCurrentUserAccountType.d.ts +3 -0
  75. package/dist/hooks/user/useCurrentUserAccountType.d.ts.map +1 -0
  76. package/dist/hooks/user/useCurrentUserAccountType.js +30 -0
  77. package/dist/locales/vi/common.json +12 -2
  78. package/dist/services/query.d.ts +5 -0
  79. package/dist/services/query.d.ts.map +1 -1
  80. package/dist/services/query.js +5 -0
  81. package/dist/services/routes.d.ts +5 -0
  82. package/dist/services/routes.d.ts.map +1 -1
  83. package/dist/services/routes.js +5 -0
  84. package/dist/store/conversation.d.ts.map +1 -1
  85. package/dist/store/conversation.js +41 -12
  86. package/dist/store/session.js +1 -1
  87. package/dist/styles/global.css +1 -1
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/dist/types/chat.d.ts +18 -1
  90. package/dist/types/chat.d.ts.map +1 -1
  91. package/dist/types/chat.js +9 -0
  92. package/dist/types/dto.d.ts +87 -0
  93. package/dist/types/dto.d.ts.map +1 -1
  94. package/dist/utils/events.d.ts +1 -0
  95. package/dist/utils/events.d.ts.map +1 -1
  96. package/dist/utils/messageTransform.d.ts +5 -0
  97. package/dist/utils/messageTransform.d.ts.map +1 -0
  98. package/dist/utils/messageTransform.js +106 -0
  99. package/dist/utils/queryHelpers.d.ts.map +1 -1
  100. package/dist/utils/queryHelpers.js +2 -0
  101. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CannedResponseBody.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseBody.tsx"],"names":[],"mappings":"AAaA,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,kBAAkB,GAAI,OAAO,wBAAwB,4CA6J1D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"CannedResponseBody.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseBody.tsx"],"names":[],"mappings":"AAiBA,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AASD,QAAA,MAAM,kBAAkB,GAAI,OAAO,wBAAwB,4CA8M1D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Empty, Segmented, Spin } from "antd";
3
3
  import { useFetchMyTeams } from "../../hooks/team/useFetchMyTeam";
4
4
  import { useTranslation } from "react-i18next";
5
- import { useCallback, useEffect, useMemo, useState } from "react";
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { CannedResponseVisibleScope } from "../../types/chat";
7
7
  import TeamItem from "./team/TeamItem";
8
8
  import { useFetchCannedResponse } from "../../hooks/cannedResponse/useFetchCannedResponse";
@@ -10,9 +10,24 @@ import InfiniteScroll from "react-infinite-scroll-component";
10
10
  import { Icon } from "../icon";
11
11
  import { sanitizeHtml } from "../../utils/common";
12
12
  import { useDebounce } from "ahooks";
13
+ import clsx from "clsx";
14
+ import emitter from "../../utils/events";
15
+ import { isArray } from "lodash";
16
+ import { useMessageFooterContext } from "../message/footer";
17
+ const parseIndex = (index, length) => {
18
+ if (index === null)
19
+ return null;
20
+ if (index < 0)
21
+ return 0;
22
+ if (index >= length)
23
+ return length - 1;
24
+ return index;
25
+ };
13
26
  const CannedResponseBody = (props) => {
14
27
  var _a, _b;
15
28
  const { search, onSelectCannedResponse, cannedQuery } = props;
29
+ const { activeCannedIndex, setActiveCannedIndex, setMaxActiveCannedIndex } = useMessageFooterContext();
30
+ const itemRefs = useRef([]);
16
31
  const { t } = useTranslation();
17
32
  const { data: myTeams, isLoading: isLoadingTeams } = useFetchMyTeams();
18
33
  const [scope, setScope] = useState(CannedResponseVisibleScope.TEAM);
@@ -25,6 +40,9 @@ const CannedResponseBody = (props) => {
25
40
  categoryId: selectedCategoryId,
26
41
  shortcut: search || cannedQueryDebounced || "",
27
42
  });
43
+ const validActiveCannedIndex = useMemo(() => {
44
+ return parseIndex(activeCannedIndex, (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0);
45
+ }, [activeCannedIndex, cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length]);
28
46
  const options = useMemo(() => {
29
47
  return [
30
48
  { label: t("team"), value: CannedResponseVisibleScope.TEAM },
@@ -43,12 +61,45 @@ const CannedResponseBody = (props) => {
43
61
  setSelectedTeamId(myTeams[0].teamId);
44
62
  }
45
63
  }, [isLoadingTeams, myTeams]);
46
- return (_jsxs("div", { className: "flex flex-1 flex-row h-[400px]", children: [_jsxs("div", { className: "w-[170px] p-2 border-r border-gray-200", children: [_jsx(Segmented, { options: options, block: true, size: "small", value: scope, onChange: setScope }), _jsx("div", { className: "overflow-y-auto h-full", children: scope === CannedResponseVisibleScope.TEAM && (_jsx("div", { className: "mt-1 flex flex-col", children: (_a = myTeams === null || myTeams === void 0 ? void 0 : myTeams.map) === null || _a === void 0 ? void 0 : _a.call(myTeams, (team, index) => renderTeamItem(team, index)) })) })] }), _jsxs("div", { id: "scrollableCannedDiv", className: "flex flex-col flex-1 h-full overflow-y-auto", children: [!!cannedQuery && !search && (_jsx("div", { className: "px-3 py-2 border-b border-gray-200", children: _jsxs("span", { className: "text-sm text-gray-500", children: [t("canned_response_quick_search_placeholder"), _jsx("span", { className: "text-blue-600", children: ` #${cannedQuery}` })] }) })), _jsx(InfiniteScroll, { dataLength: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableCannedDiv", children: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_canned_response") }) })) : ((_b = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.map) === null || _b === void 0 ? void 0 : _b.call(cannedResponses, (canned) => {
64
+ useEffect(() => {
65
+ const handler = (index) => {
66
+ var _a;
67
+ const validIndex = parseIndex(index, cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length);
68
+ if (validIndex !== null && !!(cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses[validIndex])) {
69
+ const htmlContent = ((_a = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses[validIndex]) === null || _a === void 0 ? void 0 : _a.content) || "";
70
+ // 🔒 Sanitize HTML to prevent XSS attacks
71
+ const sanitizedContent = sanitizeHtml(htmlContent);
72
+ onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent);
73
+ }
74
+ };
75
+ emitter.on("CANNED_RESPONSE_SELECT", handler);
76
+ return () => {
77
+ emitter.off("CANNED_RESPONSE_SELECT", handler);
78
+ };
79
+ }, [cannedResponses, onSelectCannedResponse]);
80
+ useEffect(() => {
81
+ setActiveCannedIndex(0);
82
+ }, [search, cannedQueryDebounced, setActiveCannedIndex]);
83
+ useEffect(() => {
84
+ var _a, _b;
85
+ if (activeCannedIndex !== null) {
86
+ (_b = (_a = itemRefs.current) === null || _a === void 0 ? void 0 : _a[activeCannedIndex]) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
87
+ block: "nearest",
88
+ });
89
+ }
90
+ }, [activeCannedIndex]);
91
+ useEffect(() => {
92
+ setMaxActiveCannedIndex((cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) - 1 > 0 ? (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) - 1 : 0);
93
+ }, [cannedResponses, setMaxActiveCannedIndex]);
94
+ return (_jsxs("div", { className: "flex flex-1 flex-row h-[400px]", children: [_jsxs("div", { className: "w-[170px] p-2 border-r border-gray-200", children: [_jsx(Segmented, { options: options, block: true, size: "small", value: scope, onChange: setScope }), _jsx("div", { className: "overflow-y-auto h-full", children: scope === CannedResponseVisibleScope.TEAM && (_jsx("div", { className: "mt-1 flex flex-col", children: (_a = myTeams === null || myTeams === void 0 ? void 0 : myTeams.map) === null || _a === void 0 ? void 0 : _a.call(myTeams, (team, index) => renderTeamItem(team, index)) })) })] }), _jsxs("div", { id: "scrollableCannedDiv", className: "flex flex-col flex-1 h-full overflow-y-auto", children: [!!cannedQuery && !search && (_jsx("div", { className: "px-3 py-2 border-b border-gray-200", children: _jsxs("span", { className: "text-sm text-gray-500", children: [t("canned_response_quick_search_placeholder"), _jsx("span", { className: "text-blue-600", children: ` #${cannedQuery}` })] }) })), _jsx(InfiniteScroll, { dataLength: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableCannedDiv", children: !isArray(cannedResponses) || (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_canned_response") }) })) : ((_b = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.map) === null || _b === void 0 ? void 0 : _b.call(cannedResponses, (canned, index) => {
47
95
  var _a, _b;
96
+ const isActive = index === validActiveCannedIndex;
48
97
  const htmlContent = (canned === null || canned === void 0 ? void 0 : canned.content) || "";
49
98
  // 🔒 Sanitize HTML to prevent XSS attacks
50
99
  const sanitizedContent = sanitizeHtml(htmlContent);
51
- return (_jsxs("div", { className: "flex flex-col flex-1 px-3 py-2 cursor-pointer hover:bg-blue-100 w-full border-b border-gray-200", onClick: () => onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent), children: [_jsxs("span", { className: "text-xs truncate text-gray-500", children: [`${((_a = canned === null || canned === void 0 ? void 0 : canned.category) === null || _a === void 0 ? void 0 : _a.name)
100
+ return (_jsxs("div", { ref: (el) => {
101
+ itemRefs.current[index] = el;
102
+ }, className: clsx("flex flex-col flex-1 px-3 py-2 cursor-pointer hover:bg-blue-100 w-full border-b border-gray-200", isActive && "bg-blue-100"), onClick: () => onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent), children: [_jsxs("span", { className: "text-xs truncate text-gray-500", children: [`${((_a = canned === null || canned === void 0 ? void 0 : canned.category) === null || _a === void 0 ? void 0 : _a.name)
52
103
  ? `${(_b = canned === null || canned === void 0 ? void 0 : canned.category) === null || _b === void 0 ? void 0 : _b.name} / `
53
104
  : ""}`, _jsx("span", { className: "text-black", children: `${(canned === null || canned === void 0 ? void 0 : canned.name) || ""} ` }), _jsx("span", { children: "/" }), _jsx("span", { className: "text-blue-500", children: ` #${(canned === null || canned === void 0 ? void 0 : canned.shortcut) || ""}` })] }), _jsx("div", { dangerouslySetInnerHTML: {
54
105
  __html: sanitizedContent,
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAQpD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,kBAEhC,8BAA8B,mDA2FhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AASpD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,kBAEhC,8BAA8B,mDAgGhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -6,6 +6,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
6
6
  import { Avatar, Badge } from "antd";
7
7
  import { useChatContext } from "../../context/ChatContext";
8
8
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
9
+ import { useConversationPreview } from "../../hooks/conversation/useConversationPreview";
9
10
  import { formatTimestamp, parseLatestMessage } from "../../utils/common";
10
11
  const ConversationBySessionItem = ({ sessionItem, }) => {
11
12
  var _a;
@@ -30,10 +31,14 @@ const ConversationBySessionItem = ({ sessionItem, }) => {
30
31
  setSelectedConversationId(conversation.conversationID);
31
32
  };
32
33
  const { avatar, displayName = "" } = useConversationDisplayData(conversation || null);
34
+ const { latestMsg, latestMsgSendTime } = useConversationPreview({
35
+ conversation,
36
+ ownerUserID: sessionItem.ownerId,
37
+ });
33
38
  if (!conversation)
34
39
  return null;
35
- return (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-100 cursor-pointer transition-colors ${isSelected ? "bg-blue-50" : "bg-white"}`, children: [isSelected && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "relative flex-shrink-0", children: _jsx(Badge, { dot: true, status: "success", offset: [-2, 36], children: _jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }) }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: displayName }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: parseLatestMessage(conversation.latestMsg, user === null || user === void 0 ? void 0 : user.userID, t) })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [_jsx("span", { className: "text-xs text-gray-400", children: formatTimestamp(conversation.latestMsgSendTime, {
40
+ return (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-100 cursor-pointer transition-colors ${isSelected ? "bg-blue-50" : "bg-white"}`, children: [isSelected && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "relative flex-shrink-0", children: _jsx(Badge, { dot: true, status: "success", offset: [-2, 36], children: _jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }) }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: displayName }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: parseLatestMessage(latestMsg, user === null || user === void 0 ? void 0 : user.userID, t) })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [latestMsgSendTime > 0 && (_jsx("span", { className: "text-xs text-gray-400", children: formatTimestamp(latestMsgSendTime, {
36
41
  hasTime: true,
37
- }) }), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.conversationID));
42
+ }) })), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.conversationID));
38
43
  };
39
44
  export default ConversationBySessionItem;
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAkBA,QAAA,MAAM,oBAAoB,+CAwLzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAoBA,QAAA,MAAM,oBAAoB,+CAoMzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -12,10 +12,12 @@ import useSessionStore from "../../store/session";
12
12
  import InfiniteScroll from "react-infinite-scroll-component";
13
13
  import ConversationBySessionItem from "./ConversationBySessionItem";
14
14
  import { useGetSession } from "../../hooks/session/useGetSession";
15
+ import { useGetTeamSessionSummary } from "../../hooks/session/useGetTeamSessionSummary";
15
16
  import { DChatSDK } from "../../constants/sdk";
16
17
  import emitter from "../../utils/events";
18
+ import { getFilterConfig } from "../session/sessionMenuItems";
17
19
  const DeskConversationList = () => {
18
- var _a;
20
+ var _a, _b;
19
21
  const searchInputRef = useRef(null);
20
22
  const { t } = useTranslation();
21
23
  const [search, setSearch] = useState("");
@@ -26,9 +28,13 @@ const DeskConversationList = () => {
26
28
  const conversationList = useConversationStore((state) => state.conversationList);
27
29
  const updateConversationList = useConversationStore((state) => state.updateConversationList);
28
30
  const filterSummary = useSessionStore((state) => state.filterSummary);
29
- const { dataFlatten: sessions, hasNextPage, fetchNextPage, refetch, } = useGetSession({
30
- filter: filterSummary,
31
- });
31
+ const { data: teamSummaries } = useGetTeamSessionSummary();
32
+ const { dataFlatten: sessions, hasNextPage, fetchNextPage, refetch } = useGetSession({ filter: filterSummary });
33
+ const currentFilterKey = filterSummary.status || filterSummary.tag || "";
34
+ const filterConfig = getFilterConfig(currentFilterKey);
35
+ const pathName = filterSummary.teamId
36
+ ? ((_a = teamSummaries === null || teamSummaries === void 0 ? void 0 : teamSummaries.find((team) => team.teamId === filterSummary.teamId)) === null || _a === void 0 ? void 0 : _a.teamName) || ""
37
+ : t("my_messages");
32
38
  const debouncedSearch = useDebounce(search, { wait: 500 });
33
39
  const onCloseSearch = () => {
34
40
  setSearch("");
@@ -65,28 +71,27 @@ const DeskConversationList = () => {
65
71
  }
66
72
  }, [searchParams, conversationList]);
67
73
  useEffect(() => {
68
- emitter.on("UPDATE_SESSION", (sessionUpdated) => {
74
+ const handler = (sessionUpdated) => {
69
75
  if (sessionUpdated.status !== filterSummary.status ||
70
76
  sessionUpdated.tag !== filterSummary.tag) {
71
77
  refetch();
72
78
  }
73
- });
79
+ };
80
+ emitter.on("UPDATE_SESSION", handler);
74
81
  return () => {
75
- emitter.off("UPDATE_SESSION", () => {
76
- refetch();
77
- });
82
+ emitter.off("UPDATE_SESSION", handler);
78
83
  };
79
- }, [filterSummary]);
80
- return (_jsxs("div", { className: `flex flex-col h-full bg-white border-r border-gray-200 w-[320px]`, children: [_jsx("div", { className: "p-3 border-b border-gray-100", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { ref: searchInputRef, placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), onChange: (e) => {
81
- if (!showSearch && e.target.value) {
82
- setShowSearchTrue();
83
- }
84
- setSearch(e.target.value);
85
- }, className: "rounded-lg text-sm flex-1 h-[36px]", size: "large", allowClear: true, onClick: setShowSearchTrue, value: search, autoFocus: false, onClear: onCloseSearch }), showSearch && (_jsx(Button, { onClick: onCloseSearch, variant: "outlined", className: "p-0 w-[36px] h-[36px] text-gray-500", children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] }) }), _jsxs("div", { id: "scrollableConversationsDiv", style: {
84
+ }, [filterSummary, refetch]);
85
+ return (_jsxs("div", { className: "flex flex-col h-full bg-white border-r border-gray-200 w-[320px]", children: [_jsxs("div", { className: "p-3 border-b border-gray-100", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { ref: searchInputRef, placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), onChange: (e) => {
86
+ if (!showSearch && e.target.value) {
87
+ setShowSearchTrue();
88
+ }
89
+ setSearch(e.target.value);
90
+ }, className: "rounded-lg text-sm flex-1 h-[36px]", size: "large", allowClear: true, onClick: setShowSearchTrue, value: search, autoFocus: false, onClear: onCloseSearch }), showSearch && (_jsx(Button, { onClick: onCloseSearch, variant: "outlined", className: "p-0 w-[36px] h-[36px] text-gray-500", children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] }), _jsxs("div", { className: "flex items-center justify-between mt-2", children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate", children: pathName }), filterConfig && (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: filterConfig.iconName, size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t(filterConfig.labelKey) })] }))] })] }), _jsxs("div", { id: "scrollableConversationsDiv", style: {
86
91
  height: "100%",
87
92
  overflow: showSearch ? "hidden" : "auto",
88
93
  position: "relative",
89
- }, children: [_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((_a = sessions === null || sessions === void 0 ? void 0 : sessions.map) === null || _a === void 0 ? void 0 : _a.call(sessions, (session) => (_jsx(ConversationBySessionItem, { sessionItem: session }, session.conversationId)))) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
94
+ }, children: [_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((_b = sessions === null || sessions === void 0 ? void 0 : sessions.map) === null || _b === void 0 ? void 0 : _b.call(sessions, (session) => (_jsx(ConversationBySessionItem, { sessionItem: session }, session.conversationId)))) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
90
95
  body: {
91
96
  padding: 0,
92
97
  },
@@ -1,8 +1,8 @@
1
1
  import { SessionStatus, SessionTag } from "../../types/chat";
2
- import { ISessionByStatus } from "../../store/type";
2
+ import { ISessionResponse } from "../../types/dto";
3
3
  interface MessageHeaderProps {
4
4
  onClose?: () => void;
5
- currentSession?: ISessionByStatus;
5
+ currentSession?: ISessionResponse;
6
6
  }
7
7
  type SelectSessionValueType = SessionStatus | SessionTag;
8
8
  export interface SelectSessionOption {
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIpD,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED,KAAK,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,QAAA,MAAM,aAAa,GAAI,6BAA6B,kBAAkB,4CA4LrE,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKnD,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED,KAAK,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,QAAA,MAAM,aAAa,GAAI,6BAA6B,kBAAkB,4CAoMrE,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -12,13 +12,16 @@ import SelectSession from "./SelectSession";
12
12
  import { useUpdateSession } from "../../hooks/session/useUpdateSession";
13
13
  import useAuthStore from "../../store/auth";
14
14
  import SearchDrawer from "../searchConversation/SearchDrawer";
15
+ import { isGroupSession } from "../../utils/imCommon";
15
16
  const MessageHeader = ({ onClose, currentSession }) => {
16
- var _a;
17
+ var _a, _b;
17
18
  const { t } = useTranslation();
18
19
  const isCx = useAuthStore((state) => state.isCx);
19
20
  const conversationData = useConversationStore((state) => state.conversationData);
21
+ const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
20
22
  const { mutate: updateSession } = useUpdateSession();
21
23
  const { avatar, displayName } = useConversationDisplayData(conversationData);
24
+ const shouldShowMemberCount = isGroupSession(conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType);
22
25
  const [currentSessionStatus, setCurrentSessionStatus] = useState(SessionStatus.UNASSIGNED);
23
26
  const [currentSessionTag, setCurrentSessionTag] = useState(SessionTag.NONE);
24
27
  const statusOptions = useMemo(() => {
@@ -122,6 +125,6 @@ const MessageHeader = ({ onClose, currentSession }) => {
122
125
  setCurrentSessionStatus(currentSession.status);
123
126
  }
124
127
  }, [currentSession]);
125
- return (_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3 bg-white flex-wrap", children: [_jsx(Avatar, { src: avatar, size: "large", className: "min-w-10 min-h-10", children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), _jsxs("div", { className: "flex flex-col overflow-hidden flex-1", children: [_jsx("p", { className: "text-base truncate", children: displayName || "" }), _jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count: 2 }) })] }), _jsxs("div", { className: "flex items-center gap-2 justify-end overflow-hidden", children: [isCx && (_jsx(SelectSession, { placeholder: t("select_tag"), options: tagOptions, value: currentSessionTag, onChange: (value) => handleUpdateSession(value, "tag", value === currentSessionTag), excludeOptions: [SessionTag.SLOW_PROCESSING, SessionTag.NONE] })), isCx && (_jsx(SelectSession, { placeholder: t("select_status"), options: statusOptions, value: currentSessionStatus, onChange: (value) => handleUpdateSession(value, "status", value === currentSessionStatus) })), _jsx(SearchDrawer, {}), _jsx(MediaCollection, {}), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "align-justify-o", size: 22 }) }), !!onClose && (_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] })] }));
128
+ return (_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3 bg-white flex-wrap", children: [_jsx(Avatar, { src: avatar, size: "large", className: "min-w-10 min-h-10", children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), _jsxs("div", { className: "flex flex-col overflow-hidden flex-1", children: [_jsx("p", { className: "text-base truncate", children: displayName || "" }), shouldShowMemberCount && (_jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count: (_b = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.memberCount) !== null && _b !== void 0 ? _b : 0 }) }))] }), _jsxs("div", { className: "flex items-center gap-2 justify-end overflow-hidden", children: [isCx && (_jsx(SelectSession, { placeholder: t("select_tag"), options: tagOptions, value: currentSessionTag, onChange: (value) => handleUpdateSession(value, "tag", value === currentSessionTag), excludeOptions: [SessionTag.SLOW_PROCESSING, SessionTag.NONE] })), isCx && (_jsx(SelectSession, { placeholder: t("select_status"), options: statusOptions, value: currentSessionStatus, onChange: (value) => handleUpdateSession(value, "status", value === currentSessionStatus) })), _jsx(SearchDrawer, {}), _jsx(MediaCollection, {}), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "align-justify-o", size: 22 }) }), !!onClose && (_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] })] }));
126
129
  };
127
130
  export default MessageHeader;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAyBA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAGD,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAiT3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AA0BA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAGD,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CA4U3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useMessage } from "../../hooks/message/useMessage";
4
- import { Empty, Modal, Spin } from "antd";
5
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { useConversationMessages } from "../../hooks/message/useConversationMessages";
4
+ import { Button, Empty, Modal, Spin } from "antd";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
6
  import dayjs from "dayjs";
7
7
  import isToday from "dayjs/plugin/isToday";
8
8
  import emitter from "../../utils/events";
@@ -18,8 +18,9 @@ import { useBoolean, useDebounceFn } from "ahooks";
18
18
  import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../constants";
19
19
  import { markConversationMessageAsRead } from "../../hooks/conversation/useConversation";
20
20
  import { useChatContext } from "../../context/ChatContext";
21
- import { useGetSession } from "../../hooks/session/useGetSession";
22
21
  import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
22
+ import { useConversationSessionState } from "../../hooks/session/useConversationSessionState";
23
+ import { useJoinGroupFlow } from "../../hooks/session/useJoinGroupFlow";
23
24
  dayjs.extend(isToday);
24
25
  const BOTTOM_THRESHOLD = -5;
25
26
  const MessageList = (props) => {
@@ -28,19 +29,31 @@ const MessageList = (props) => {
28
29
  const { user } = useChatContext();
29
30
  const { onClose, conversationId, searchClientMsgID, openCreateCannedModal } = props;
30
31
  const scrollRef = useRef(null);
31
- const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useMessage(conversationId, searchClientMsgID);
32
32
  const conversationData = useConversationStore((state) => state.conversationData);
33
33
  const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
34
34
  const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
35
- const { dataFlatten: sessions, refetch: refetchSession } = useGetSession({
36
- filter: {
37
- conversationIds: !!conversationId ? [conversationId] : [],
38
- },
39
- });
40
35
  const [openMenuId, setOpenMenuId] = useState(null);
41
36
  const [selectedItem, setSelectedItem] = useState(null);
42
37
  const [showConfirmRevoke, { setTrue: openConfirmRevoke, setFalse: closeConfirmRevoke },] = useBoolean(false);
43
38
  const { revokeMessage, loading: isRevoking } = useRevokeMessage();
39
+ const { isNotGroupMember, latestConversationSession, customerUserID, refetchConversationSessions, refetchIsJoined, } = useConversationSessionState({
40
+ conversationId,
41
+ conversationType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType,
42
+ groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
43
+ userID: user === null || user === void 0 ? void 0 : user.userID,
44
+ });
45
+ const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useConversationMessages({
46
+ conversationId,
47
+ searchClientMsgID,
48
+ restrictToLastSession: isNotGroupMember,
49
+ customerUserID,
50
+ });
51
+ const { handleJoinGroup, isJoining } = useJoinGroupFlow({
52
+ groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
53
+ latestConversationSessionId: latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.id,
54
+ refetchConversationSessions,
55
+ refetchIsJoined,
56
+ });
44
57
  const handleOpenRevoke = useCallback((clientMsgID) => {
45
58
  setSelectedItem(clientMsgID);
46
59
  openConfirmRevoke();
@@ -58,9 +71,6 @@ const MessageList = (props) => {
58
71
  revokeMessage,
59
72
  handleCloseRevoke,
60
73
  ]);
61
- const currentSession = useMemo(() => {
62
- return sessions === null || sessions === void 0 ? void 0 : sessions.find((session) => session.conversationId === conversationId);
63
- }, [sessions, conversationId]);
64
74
  const handleMarkConversationMessageAsRead = () => {
65
75
  var _a, _b, _c, _d, _e;
66
76
  const lastMessage = (_b = (_a = latestLoadState === null || latestLoadState === void 0 ? void 0 : latestLoadState.current) === null || _a === void 0 ? void 0 : _a.messageList) === null || _b === void 0 ? void 0 : _b[0];
@@ -136,17 +146,16 @@ const MessageList = (props) => {
136
146
  };
137
147
  }, []);
138
148
  useEffect(() => {
139
- emitter.on("UPDATE_SESSION", (sessionUpdated) => {
149
+ const handler = (sessionUpdated) => {
140
150
  if (sessionUpdated.conversationId === conversationId) {
141
- refetchSession();
151
+ refetchConversationSessions();
142
152
  }
143
- });
153
+ };
154
+ emitter.on("UPDATE_SESSION", handler);
144
155
  return () => {
145
- emitter.off("UPDATE_SESSION", () => {
146
- refetchSession();
147
- });
156
+ emitter.off("UPDATE_SESSION", handler);
148
157
  };
149
- }, [conversationId]);
158
+ }, [conversationId, refetchConversationSessions]);
150
159
  useEffect(() => {
151
160
  if (!loadState.hasMoreNew && !loadState.initLoading) {
152
161
  handleMarkConversationMessageAsRead();
@@ -174,7 +183,7 @@ const MessageList = (props) => {
174
183
  backgroundSize: "cover",
175
184
  backgroundPosition: "center",
176
185
  overflowX: "hidden",
177
- }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: currentSession }), _jsx("div", { id: "scrollableMessagesDiv", ref: scrollRef, style: {
186
+ }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession }), _jsx("div", { id: "scrollableMessagesDiv", ref: scrollRef, style: {
178
187
  height: "100%",
179
188
  overflowY: "auto",
180
189
  overflowX: "hidden",
@@ -192,6 +201,6 @@ const MessageList = (props) => {
192
201
  handleMarkConversationMessageAsRead();
193
202
  loadMoreNewMessage();
194
203
  }
195
- }, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), _jsx(MessageFooter, { currentSession: currentSession, openCreateCannedModal: openCreateCannedModal }), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
204
+ }, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isNotGroupMember ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: handleJoinGroup, loading: isJoining, children: t("join_group") })] })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
196
205
  };
197
206
  export default MessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"SelectSession.d.ts","sourceRoot":"","sources":["../../../src/components/message/SelectSession.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,UAAU,kBAAkB;IAC1B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,aAAa,GAAI,4DAMpB,kBAAkB,4CAwDpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"SelectSession.d.ts","sourceRoot":"","sources":["../../../src/components/message/SelectSession.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,UAAU,kBAAkB;IAC1B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,aAAa,GAAI,4DAMpB,kBAAkB,4CAwDpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -3,23 +3,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Select } from "antd";
4
4
  import { Icon } from "../icon";
5
5
  import clsx from "clsx";
6
- import { useTranslation } from "react-i18next";
7
6
  const SelectSession = ({ options, value, onChange, excludeOptions, placeholder, }) => {
8
- const { t } = useTranslation();
9
7
  const selectedOption = options.find((option) => option.value === value);
10
- // Filter out excluded options
11
- const filteredOptions = options.filter((option) => !(excludeOptions === null || excludeOptions === void 0 ? void 0 : excludeOptions.includes(option.value)));
12
- // Transform options for Ant Design Select
8
+ const renderOptionLabel = (option) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: clsx("w-2 h-2 rounded-full", option.tintColorClassnameBg) }), _jsx("span", { className: clsx("text-xs truncate flex-1", option.tintColorClassname), children: option.label })] }));
9
+ const filteredOptions = options.filter((option) => option.value === value || !(excludeOptions === null || excludeOptions === void 0 ? void 0 : excludeOptions.includes(option.value)));
13
10
  const selectOptions = filteredOptions.map((option) => ({
14
- label: (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: clsx("w-2 h-2 rounded-full", option.tintColorClassnameBg) }), _jsx("span", { className: clsx("text-xs truncate flex-1", option.tintColorClassname), children: option.label })] })),
11
+ label: renderOptionLabel(option),
15
12
  value: option.value,
16
13
  }));
17
- // Handle selection change
18
14
  const handleSelect = (val) => {
19
15
  onChange(val);
20
16
  };
21
- return (_jsx("div", { className: "relative", children: _jsx(Select, { className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.bgTintColorClassname} custom-select min-w-[100px]`, placement: "bottomLeft", placeholder: placeholder, options: selectOptions, value: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value, onSelect: handleSelect, suffixIcon: _jsx(Icon, { icon: "angle-down-o", className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.tintColorClassname} mt-[2px]`, size: 14 }), menuItemSelectedIcon: null, labelRender: (option) => ["NONE", ""].includes(option.value)
17
+ return (_jsx("div", { className: "relative", children: _jsx(Select, { className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.bgTintColorClassname} custom-select min-w-[100px]`, placement: "bottomLeft", placeholder: placeholder, options: selectOptions, value: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value, onSelect: handleSelect, suffixIcon: _jsx(Icon, { icon: "angle-down-o", className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.tintColorClassname} mt-[2px]`, size: 14 }), menuItemSelectedIcon: null, labelRender: () => ["NONE", ""].includes(selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value)
22
18
  ? placeholder
23
- : option.label }) }));
19
+ : selectedOption
20
+ ? renderOptionLabel(selectedOption)
21
+ : undefined }) }));
24
22
  };
25
23
  export default SelectSession;
@@ -1 +1 @@
1
- {"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAsBA,QAAA,MAAM,SAAS,+CA0Gd,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAsBA,QAAA,MAAM,SAAS,+CA2Gd,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -13,7 +13,7 @@ import MediaActions from "./MediaActions";
13
13
  import useConversationStore from "../../../store/conversation";
14
14
  const ActionBar = () => {
15
15
  const [editor] = useLexicalComposerContext();
16
- const { onSendMessage, listUploadFiles, setListUploadFiles } = useMessageFooterContext();
16
+ const { onSendMessage, isInternal, listUploadFiles, setListUploadFiles } = useMessageFooterContext();
17
17
  const quotedMessage = useConversationStore((state) => state.quotedMessage);
18
18
  const [isEmptyInput, setIsEmptyInput] = useState(true);
19
19
  const canSend = useMemo(() => {
@@ -34,6 +34,7 @@ const ActionBar = () => {
34
34
  plainText,
35
35
  richText,
36
36
  type: listUploadFiles.length > 0 ? "file" : "text",
37
+ isInternal,
37
38
  });
38
39
  }
39
40
  editor.update(() => {
@@ -71,7 +72,7 @@ const ActionBar = () => {
71
72
  }
72
73
  }
73
74
  });
74
- }, [editor, onSendMessage, canSend]);
75
+ }, [editor, onSendMessage, canSend, isInternal, listUploadFiles.length]);
75
76
  useEffect(() => {
76
77
  return editor.registerUpdateListener(({ editorState }) => {
77
78
  editorState.read(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"CannedResponsePlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/CannedResponsePlugin.tsx"],"names":[],"mappings":"AAIA,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,QAiCrE"}
1
+ {"version":3,"file":"CannedResponsePlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/CannedResponsePlugin.tsx"],"names":[],"mappings":"AAWA,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,QAsFrE"}
@@ -1,8 +1,10 @@
1
1
  import { useEffect } from "react";
2
2
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
- import { $getSelection, $isRangeSelection } from "lexical";
3
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, } from "lexical";
4
+ import { useMessageFooterContext } from ".";
4
5
  export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
5
6
  const [editor] = useLexicalComposerContext();
7
+ const { activeCannedIndex, setActiveCannedIndex, isOpenCanned, maxActiveCannedIndex, } = useMessageFooterContext();
6
8
  useEffect(() => {
7
9
  return editor.registerUpdateListener(({ editorState }) => {
8
10
  editorState.read(() => {
@@ -27,5 +29,39 @@ export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
27
29
  });
28
30
  });
29
31
  }, [editor, onOpen, onClose]);
32
+ useEffect(() => {
33
+ const unregisterUp = editor.registerCommand(KEY_ARROW_UP_COMMAND, (e) => {
34
+ if (!isOpenCanned)
35
+ return false;
36
+ e.preventDefault();
37
+ setActiveCannedIndex(activeCannedIndex === null
38
+ ? null
39
+ : activeCannedIndex - 1 < 0
40
+ ? 0
41
+ : activeCannedIndex - 1);
42
+ return true;
43
+ }, COMMAND_PRIORITY_LOW);
44
+ const unregisterDown = editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (e) => {
45
+ if (!isOpenCanned)
46
+ return false;
47
+ e.preventDefault();
48
+ setActiveCannedIndex(activeCannedIndex === null
49
+ ? 0
50
+ : activeCannedIndex + 1 > maxActiveCannedIndex
51
+ ? maxActiveCannedIndex
52
+ : activeCannedIndex + 1);
53
+ return true;
54
+ }, COMMAND_PRIORITY_LOW);
55
+ return () => {
56
+ unregisterUp();
57
+ unregisterDown();
58
+ };
59
+ }, [
60
+ editor,
61
+ isOpenCanned,
62
+ activeCannedIndex,
63
+ setActiveCannedIndex,
64
+ maxActiveCannedIndex,
65
+ ]);
30
66
  return null;
31
67
  }
@@ -0,0 +1,7 @@
1
+ interface ComposerEditorProps {
2
+ isInternal: boolean;
3
+ placeholderText: string;
4
+ }
5
+ declare const ComposerEditor: ({ isInternal, placeholderText, }: ComposerEditorProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default ComposerEditor;
7
+ //# sourceMappingURL=ComposerEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComposerEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ComposerEditor.tsx"],"names":[],"mappings":"AAOA,UAAU,mBAAmB;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,QAAA,MAAM,cAAc,GAAI,kCAGrB,mBAAmB,4CAwBrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
4
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
5
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
6
+ import clsx from "clsx";
7
+ const ComposerEditor = ({ isInternal, placeholderText, }) => {
8
+ const editorClasses = clsx("min-h-[64px] max-h-[140px] overflow-y-auto rounded-md border px-3 py-2 text-sm", isInternal
9
+ ? "border-orange-300 bg-orange-50"
10
+ : "border-indigo-500 bg-blue-100");
11
+ return (_jsx("div", { className: "px-4", children: _jsx("div", { className: "relative", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: editorClasses }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": placeholderText, placeholder: _jsx("div", { className: "absolute left-3 top-2 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: placeholderText }) }) }) }) }));
12
+ };
13
+ export default ComposerEditor;
@@ -0,0 +1,9 @@
1
+ interface ComposerTabsProps {
2
+ canUseInternal: boolean;
3
+ hintText: string;
4
+ isInternal: boolean;
5
+ setIsInternal: (isInternal: boolean) => void;
6
+ }
7
+ declare const ComposerTabs: ({ canUseInternal, hintText, isInternal, setIsInternal, }: ComposerTabsProps) => import("react/jsx-runtime").JSX.Element | null;
8
+ export default ComposerTabs;
9
+ //# sourceMappingURL=ComposerTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComposerTabs.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ComposerTabs.tsx"],"names":[],"mappings":"AAOA,UAAU,iBAAiB;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C;AAeD,QAAA,MAAM,YAAY,GAAI,0DAKnB,iBAAiB,mDA2CnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { Tabs } from "antd";
5
+ import { useMemo } from "react";
6
+ import { useTranslation } from "react-i18next";
7
+ const COMPOSER_TABS = [
8
+ {
9
+ isInternal: false,
10
+ activeClassName: "text-indigo-600",
11
+ labelKey: "reply",
12
+ },
13
+ {
14
+ isInternal: true,
15
+ activeClassName: "text-orange-600",
16
+ labelKey: "internal",
17
+ },
18
+ ];
19
+ const ComposerTabs = ({ canUseInternal, hintText, isInternal, setIsInternal, }) => {
20
+ const { t } = useTranslation();
21
+ const items = useMemo(() => COMPOSER_TABS.map((tab) => ({
22
+ key: String(tab.isInternal),
23
+ label: (_jsx("span", { className: clsx("text-sm font-semibold", isInternal === tab.isInternal
24
+ ? tab.activeClassName
25
+ : "text-gray-500"), children: t(tab.labelKey) })),
26
+ children: null,
27
+ })), [isInternal, t]);
28
+ if (!canUseInternal) {
29
+ return null;
30
+ }
31
+ return (_jsx(Tabs, { activeKey: String(isInternal), items: items, onChange: (value) => setIsInternal(value === "true"), tabBarExtraContent: {
32
+ left: _jsx("p", { className: "pr-4 text-sm text-gray-500", children: hintText }),
33
+ }, className: clsx("[&_.ant-tabs-content-holder]:hidden [&_.ant-tabs-ink-bar]:hidden [&_.ant-tabs-nav]:mb-0 [&_.ant-tabs-nav]:px-4 [&_.ant-tabs-nav]:pt-0 [&_.ant-tabs-nav]:before:border-b-0 [&_.ant-tabs-nav-wrap]:justify-end [&_.ant-tabs-extra-content]:flex [&_.ant-tabs-extra-content]:items-center [&_.ant-tabs-extra-content]:overflow-hidden [&_.ant-tabs-extra-content]:text-ellipsis [&_.ant-tabs-tab]:mb-0 [&_.ant-tabs-tab]:rounded-t-[16px] [&_.ant-tabs-tab]:border [&_.ant-tabs-tab]:border-b-0 [&_.ant-tabs-tab]:border-transparent [&_.ant-tabs-tab]:px-3 [&_.ant-tabs-tab]:py-1.5 [&_.ant-tabs-tab]:leading-5 [&_.ant-tabs-tab-btn]:leading-5", isInternal
34
+ ? "[&_.ant-tabs-tab-active]:border-orange-200 [&_.ant-tabs-tab-active]:bg-orange-50"
35
+ : "[&_.ant-tabs-tab-active]:border-indigo-200 [&_.ant-tabs-tab-active]:bg-indigo-50") }));
36
+ };
37
+ export default ComposerTabs;