@droppii-org/chat-sdk 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/assets/svg/cannedResponse.d.ts +7 -0
  2. package/dist/assets/svg/cannedResponse.d.ts.map +1 -0
  3. package/dist/assets/svg/cannedResponse.js +3 -0
  4. package/dist/assets/svg/cannedResponse.tsx +48 -0
  5. package/dist/assets/svg/index.d.ts +1 -0
  6. package/dist/assets/svg/index.d.ts.map +1 -1
  7. package/dist/assets/svg/index.js +1 -0
  8. package/dist/assets/svg/index.ts +1 -0
  9. package/dist/components/cannedResponse/CannedResponseBody.d.ts +8 -0
  10. package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -0
  11. package/dist/components/cannedResponse/CannedResponseBody.js +58 -0
  12. package/dist/components/cannedResponse/CannedResponseFooter.d.ts +6 -0
  13. package/dist/components/cannedResponse/CannedResponseFooter.d.ts.map +1 -0
  14. package/dist/components/cannedResponse/CannedResponseFooter.js +8 -0
  15. package/dist/components/cannedResponse/CannedResponseHeader.d.ts +8 -0
  16. package/dist/components/cannedResponse/CannedResponseHeader.d.ts.map +1 -0
  17. package/dist/components/cannedResponse/CannedResponseHeader.js +11 -0
  18. package/dist/components/cannedResponse/index.d.ts +8 -0
  19. package/dist/components/cannedResponse/index.d.ts.map +1 -0
  20. package/dist/components/cannedResponse/index.js +34 -0
  21. package/dist/components/cannedResponse/team/TeamItem.d.ts +11 -0
  22. package/dist/components/cannedResponse/team/TeamItem.d.ts.map +1 -0
  23. package/dist/components/cannedResponse/team/TeamItem.js +31 -0
  24. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  25. package/dist/components/conversation/ConversationBySessionItem.js +6 -2
  26. package/dist/components/mediaCollection/LinkCollection.js +1 -1
  27. package/dist/components/message/MediaPreviewIcon.d.ts +7 -0
  28. package/dist/components/message/MediaPreviewIcon.d.ts.map +1 -0
  29. package/dist/components/message/MediaPreviewIcon.js +24 -0
  30. package/dist/components/message/MessageHeader.js +1 -1
  31. package/dist/components/message/MessageList.d.ts +1 -0
  32. package/dist/components/message/MessageList.d.ts.map +1 -1
  33. package/dist/components/message/MessageList.js +49 -6
  34. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  35. package/dist/components/message/footer/ActionBar.js +15 -86
  36. package/dist/components/message/footer/CannedResponsePlugin.d.ts +7 -0
  37. package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -0
  38. package/dist/components/message/footer/CannedResponsePlugin.js +31 -0
  39. package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
  40. package/dist/components/message/footer/EmojiPicker.js +9 -5
  41. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  42. package/dist/components/message/footer/EnterHandler.js +16 -5
  43. package/dist/components/message/footer/FilePreview.d.ts +5 -0
  44. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  45. package/dist/components/message/footer/FilePreview.js +15 -12
  46. package/dist/components/message/footer/MediaActions.d.ts +10 -0
  47. package/dist/components/message/footer/MediaActions.d.ts.map +1 -0
  48. package/dist/components/message/footer/MediaActions.js +98 -0
  49. package/dist/components/message/footer/QuotedMessage.d.ts +2 -0
  50. package/dist/components/message/footer/QuotedMessage.d.ts.map +1 -0
  51. package/dist/components/message/footer/QuotedMessage.js +24 -0
  52. package/dist/components/message/footer/editorConfig.d.ts +24 -0
  53. package/dist/components/message/footer/editorConfig.d.ts.map +1 -0
  54. package/dist/components/message/footer/editorConfig.js +33 -0
  55. package/dist/components/message/footer/index.d.ts +2 -1
  56. package/dist/components/message/footer/index.d.ts.map +1 -1
  57. package/dist/components/message/footer/index.js +33 -28
  58. package/dist/components/message/item/QuoteMessage.d.ts +9 -0
  59. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -0
  60. package/dist/components/message/item/QuoteMessage.js +41 -0
  61. package/dist/components/message/item/RevokeMessage.d.ts +5 -0
  62. package/dist/components/message/item/RevokeMessage.d.ts.map +1 -0
  63. package/dist/components/message/item/RevokeMessage.js +8 -0
  64. package/dist/components/message/item/TextMessage.js +1 -1
  65. package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
  66. package/dist/components/message/item/UrlTextMessage.js +3 -3
  67. package/dist/components/message/item/index.d.ts +6 -1
  68. package/dist/components/message/item/index.d.ts.map +1 -1
  69. package/dist/components/message/item/index.js +79 -25
  70. package/dist/components/richTextEditor/RichTextEditor.d.ts +12 -0
  71. package/dist/components/richTextEditor/RichTextEditor.d.ts.map +1 -0
  72. package/dist/components/richTextEditor/RichTextEditor.js +62 -0
  73. package/dist/components/searchConversation/SearchDrawer.js +1 -1
  74. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts +3 -1
  75. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts.map +1 -1
  76. package/dist/components/searchConversation/item/SearchItemAsMessage.js +5 -2
  77. package/dist/components/thread/AssignConfirmModal.d.ts +12 -0
  78. package/dist/components/thread/AssignConfirmModal.d.ts.map +1 -0
  79. package/dist/components/thread/AssignConfirmModal.js +11 -0
  80. package/dist/components/thread/ManualAssignPopover.d.ts +14 -0
  81. package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -0
  82. package/dist/components/thread/ManualAssignPopover.js +83 -0
  83. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  84. package/dist/components/thread/SessionSection.js +11 -6
  85. package/dist/components/thread/UserSection.js +1 -1
  86. package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts +3 -0
  87. package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts.map +1 -0
  88. package/dist/hooks/cannedResponse/useFetchCannedCategories.js +13 -0
  89. package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts +219 -0
  90. package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts.map +1 -0
  91. package/dist/hooks/cannedResponse/useFetchCannedResponse.js +55 -0
  92. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  93. package/dist/hooks/message/useMessage.js +18 -1
  94. package/dist/hooks/message/useRevokeMessage.d.ts +5 -0
  95. package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -0
  96. package/dist/hooks/message/useRevokeMessage.js +16 -0
  97. package/dist/hooks/message/useSendMessage.d.ts +1 -0
  98. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  99. package/dist/hooks/message/useSendMessage.js +40 -9
  100. package/dist/hooks/session/useAssignSession.d.ts +8 -0
  101. package/dist/hooks/session/useAssignSession.d.ts.map +1 -0
  102. package/dist/hooks/session/useAssignSession.js +15 -0
  103. package/dist/hooks/session/useCreateNote.d.ts.map +1 -1
  104. package/dist/hooks/session/useCreateNote.js +2 -1
  105. package/dist/hooks/session/useGetTeamSupporters.d.ts +8 -0
  106. package/dist/hooks/session/useGetTeamSupporters.d.ts.map +1 -0
  107. package/dist/hooks/session/useGetTeamSupporters.js +20 -0
  108. package/dist/hooks/team/useFetchMyTeam.d.ts +3 -0
  109. package/dist/hooks/team/useFetchMyTeam.d.ts.map +1 -0
  110. package/dist/hooks/team/useFetchMyTeam.js +12 -0
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +1 -0
  114. package/dist/locales/vi/common.json +54 -53
  115. package/dist/screens/deskMessage/index.d.ts +4 -1
  116. package/dist/screens/deskMessage/index.d.ts.map +1 -1
  117. package/dist/screens/deskMessage/index.js +2 -2
  118. package/dist/services/query.d.ts +5 -0
  119. package/dist/services/query.d.ts.map +1 -1
  120. package/dist/services/query.js +5 -0
  121. package/dist/services/routes.d.ts +5 -0
  122. package/dist/services/routes.d.ts.map +1 -1
  123. package/dist/services/routes.js +5 -0
  124. package/dist/store/conversation.d.ts.map +1 -1
  125. package/dist/store/conversation.js +7 -1
  126. package/dist/styles/global.css +1 -1
  127. package/dist/tsconfig.tsbuildinfo +1 -0
  128. package/dist/types/chat.d.ts +7 -1
  129. package/dist/types/chat.d.ts.map +1 -1
  130. package/dist/types/chat.js +5 -0
  131. package/dist/types/dto.d.ts +53 -1
  132. package/dist/types/dto.d.ts.map +1 -1
  133. package/dist/utils/common.d.ts +3 -2
  134. package/dist/utils/common.d.ts.map +1 -1
  135. package/dist/utils/common.js +43 -19
  136. package/dist/utils/fileValidation.d.ts.map +1 -1
  137. package/dist/utils/fileValidation.js +2 -8
  138. package/dist/utils/queryHelpers.d.ts +3 -0
  139. package/dist/utils/queryHelpers.d.ts.map +1 -0
  140. package/dist/utils/queryHelpers.js +11 -0
  141. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ interface ISvgProps {
2
+ size?: number;
3
+ className?: string;
4
+ }
5
+ declare const CannedResponseIcon: (props: ISvgProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default CannedResponseIcon;
7
+ //# sourceMappingURL=cannedResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cannedResponse.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/cannedResponse.tsx"],"names":[],"mappings":"AAAA,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,kBAAkB,GAAI,OAAO,SAAS,4CAyC3C,CAAC;AACF,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const CannedResponseIcon = (props) => (_jsxs("svg", { width: props.size || 18, height: props.size || 18, viewBox: `${0} ${0} ${props.size || 18} ${props.size || 18}`, fill: "none", xmlns: "http://www.w3.org/2000/svg", className: props === null || props === void 0 ? void 0 : props.className, children: [_jsxs("g", { clipPath: "url(#clip0_4070_70030)", children: [_jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M6.66732 0.833374C7.12755 0.833374 7.50065 1.20647 7.50065 1.66671L5.00065 18.3334C5.00065 18.7936 4.62755 19.1667 4.16732 19.1667C3.70708 19.1667 3.33398 18.7936 3.33398 18.3334L5.83398 1.66671C5.83398 1.20647 6.20708 0.833374 6.66732 0.833374Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M14.9993 0.833374C15.4596 0.833374 15.8327 1.20647 15.8327 1.66671L13.3327 18.3334C13.3327 18.7936 12.9596 19.1667 12.4993 19.1667C12.0391 19.1667 11.666 18.7936 11.666 18.3334L14.166 1.66671C14.166 1.20647 14.5391 0.833374 14.9993 0.833374Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M19.1673 5.83333C19.1673 6.29357 18.7942 6.66667 18.334 6.66667H1.66732C1.20708 6.66667 0.833984 6.29357 0.833984 5.83333C0.833984 5.37309 1.20708 5 1.66732 5H18.334C18.7942 5 19.1673 5.3731 19.1673 5.83333Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M19.1673 14.1667C19.1673 14.627 18.7942 15 18.334 15H1.66732C1.20708 15 0.833984 14.627 0.833984 14.1667C0.833984 13.7065 1.20708 13.3334 1.66732 13.3334H18.334C18.7942 13.3334 19.1673 13.7065 19.1673 14.1667Z", fill: "currentColor" })] }), _jsx("defs", { children: _jsx("clipPath", { id: "clip0_4070_70030", children: _jsx("rect", { width: props.size || 18, height: props.size || 18, fill: "white" }) }) })] }));
3
+ export default CannedResponseIcon;
@@ -0,0 +1,48 @@
1
+ interface ISvgProps {
2
+ size?: number;
3
+ className?: string;
4
+ }
5
+
6
+ const CannedResponseIcon = (props: ISvgProps) => (
7
+ <svg
8
+ width={props.size || 18}
9
+ height={props.size || 18}
10
+ viewBox={`${0} ${0} ${props.size || 18} ${props.size || 18}`}
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ className={props?.className}
14
+ >
15
+ <g clipPath="url(#clip0_4070_70030)">
16
+ <path
17
+ fillRule="evenodd"
18
+ clipRule="evenodd"
19
+ d="M6.66732 0.833374C7.12755 0.833374 7.50065 1.20647 7.50065 1.66671L5.00065 18.3334C5.00065 18.7936 4.62755 19.1667 4.16732 19.1667C3.70708 19.1667 3.33398 18.7936 3.33398 18.3334L5.83398 1.66671C5.83398 1.20647 6.20708 0.833374 6.66732 0.833374Z"
20
+ fill="currentColor"
21
+ />
22
+ <path
23
+ fillRule="evenodd"
24
+ clipRule="evenodd"
25
+ d="M14.9993 0.833374C15.4596 0.833374 15.8327 1.20647 15.8327 1.66671L13.3327 18.3334C13.3327 18.7936 12.9596 19.1667 12.4993 19.1667C12.0391 19.1667 11.666 18.7936 11.666 18.3334L14.166 1.66671C14.166 1.20647 14.5391 0.833374 14.9993 0.833374Z"
26
+ fill="currentColor"
27
+ />
28
+ <path
29
+ fillRule="evenodd"
30
+ clipRule="evenodd"
31
+ d="M19.1673 5.83333C19.1673 6.29357 18.7942 6.66667 18.334 6.66667H1.66732C1.20708 6.66667 0.833984 6.29357 0.833984 5.83333C0.833984 5.37309 1.20708 5 1.66732 5H18.334C18.7942 5 19.1673 5.3731 19.1673 5.83333Z"
32
+ fill="currentColor"
33
+ />
34
+ <path
35
+ fillRule="evenodd"
36
+ clipRule="evenodd"
37
+ d="M19.1673 14.1667C19.1673 14.627 18.7942 15 18.334 15H1.66732C1.20708 15 0.833984 14.627 0.833984 14.1667C0.833984 13.7065 1.20708 13.3334 1.66732 13.3334H18.334C18.7942 13.3334 19.1673 13.7065 19.1673 14.1667Z"
38
+ fill="currentColor"
39
+ />
40
+ </g>
41
+ <defs>
42
+ <clipPath id="clip0_4070_70030">
43
+ <rect width={props.size || 18} height={props.size || 18} fill="white" />
44
+ </clipPath>
45
+ </defs>
46
+ </svg>
47
+ );
48
+ export default CannedResponseIcon;
@@ -1,2 +1,3 @@
1
1
  export * from "./document";
2
+ export * from "./cannedResponse";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
@@ -1 +1,2 @@
1
1
  export * from "./document";
2
+ export * from "./cannedResponse";
@@ -1 +1,2 @@
1
1
  export * from "./document";
2
+ export * from "./cannedResponse";
@@ -0,0 +1,8 @@
1
+ interface ICannedResponseBodyProps {
2
+ search?: string;
3
+ onSelectCannedResponse?: (content: string) => void;
4
+ cannedQuery?: string;
5
+ }
6
+ declare const CannedResponseBody: (props: ICannedResponseBodyProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default CannedResponseBody;
8
+ //# sourceMappingURL=CannedResponseBody.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Empty, Segmented, Spin } from "antd";
3
+ import { useFetchMyTeams } from "../../hooks/team/useFetchMyTeam";
4
+ import { useTranslation } from "react-i18next";
5
+ import { useCallback, useEffect, useMemo, useState } from "react";
6
+ import { CannedResponseVisibleScope } from "../../types/chat";
7
+ import TeamItem from "./team/TeamItem";
8
+ import { useFetchCannedResponse } from "../../hooks/cannedResponse/useFetchCannedResponse";
9
+ import InfiniteScroll from "react-infinite-scroll-component";
10
+ import { Icon } from "../icon";
11
+ import { sanitizeHtml } from "../../utils/common";
12
+ import { useDebounce } from "ahooks";
13
+ const CannedResponseBody = (props) => {
14
+ var _a, _b;
15
+ const { search, onSelectCannedResponse, cannedQuery } = props;
16
+ const { t } = useTranslation();
17
+ const { data: myTeams, isLoading: isLoadingTeams } = useFetchMyTeams();
18
+ const [scope, setScope] = useState(CannedResponseVisibleScope.TEAM);
19
+ const [selectedTeamId, setSelectedTeamId] = useState(undefined);
20
+ const [selectedCategoryId, setSelectedCategoryId] = useState(undefined);
21
+ const cannedQueryDebounced = useDebounce(cannedQuery, { wait: 300 });
22
+ const { dataFlatten: cannedResponses, fetchNextPage, hasNextPage, } = useFetchCannedResponse({
23
+ visibilityScope: scope,
24
+ teamId: selectedTeamId,
25
+ categoryId: selectedCategoryId,
26
+ shortcut: search || cannedQueryDebounced || "",
27
+ });
28
+ const options = useMemo(() => {
29
+ return [
30
+ { label: t("team"), value: CannedResponseVisibleScope.TEAM },
31
+ { label: t("personal"), value: CannedResponseVisibleScope.PRIVATE },
32
+ ];
33
+ }, [t]);
34
+ const onSelectCategory = useCallback((teamId, categoryId) => {
35
+ setSelectedTeamId(teamId);
36
+ setSelectedCategoryId(categoryId);
37
+ }, []);
38
+ const renderTeamItem = useCallback((team, index) => {
39
+ return (_jsx(TeamItem, { team: team, onSelectCategory: onSelectCategory, selectedTeamId: selectedTeamId, selectedCategoryId: selectedCategoryId, defaultOpen: index === 0 }, team === null || team === void 0 ? void 0 : team.teamId));
40
+ }, [onSelectCategory, selectedTeamId, selectedCategoryId]);
41
+ useEffect(() => {
42
+ if (!isLoadingTeams && myTeams && myTeams.length > 0) {
43
+ setSelectedTeamId(myTeams[0].teamId);
44
+ }
45
+ }, [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) => {
47
+ var _a, _b;
48
+ const htmlContent = (canned === null || canned === void 0 ? void 0 : canned.content) || "";
49
+ // 🔒 Sanitize HTML to prevent XSS attacks
50
+ 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)
52
+ ? `${(_b = canned === null || canned === void 0 ? void 0 : canned.category) === null || _b === void 0 ? void 0 : _b.name} / `
53
+ : ""}`, _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
+ __html: sanitizedContent,
55
+ }, className: "text-sm mt-1" })] }, canned === null || canned === void 0 ? void 0 : canned.id));
56
+ })) })] })] }));
57
+ };
58
+ export default CannedResponseBody;
@@ -0,0 +1,6 @@
1
+ interface ICannedResponseFooterProps {
2
+ openCreateCannedModal?: () => void;
3
+ }
4
+ declare const CannedResponseFooter: ({ openCreateCannedModal, }: ICannedResponseFooterProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default CannedResponseFooter;
6
+ //# sourceMappingURL=CannedResponseFooter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CannedResponseFooter.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseFooter.tsx"],"names":[],"mappings":"AAGA,UAAU,0BAA0B;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,oBAAoB,GAAI,4BAE3B,0BAA0B,4CAgB5B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Icon } from "../icon";
4
+ const CannedResponseFooter = ({ openCreateCannedModal, }) => {
5
+ const { t } = useTranslation();
6
+ return (_jsx("div", { className: "flex items-center justify-end border-t border-gray-200 p-2", children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1", onClick: openCreateCannedModal, children: [_jsx(Icon, { icon: "plus-circle-o", size: 16, className: "text-blue-500" }), _jsx("span", { className: "text-sm text-blue-500", children: t("add_canned_response") })] }) }));
7
+ };
8
+ export default CannedResponseFooter;
@@ -0,0 +1,8 @@
1
+ interface ICannedResponseHeaderProps {
2
+ onClose?: () => void;
3
+ search?: string;
4
+ setSearch?: (search: string) => void;
5
+ }
6
+ declare const CannedResponseHeader: (props: ICannedResponseHeaderProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default CannedResponseHeader;
8
+ //# sourceMappingURL=CannedResponseHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CannedResponseHeader.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseHeader.tsx"],"names":[],"mappings":"AAKA,UAAU,0BAA0B;IAClC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,QAAA,MAAM,oBAAoB,GAAI,OAAO,0BAA0B,4CA+B9D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import CannedResponseIcon from "../../assets/svg/cannedResponse";
4
+ import { Button, Input } from "antd";
5
+ import { Icon } from "../icon";
6
+ const CannedResponseHeader = (props) => {
7
+ const { onClose, search, setSearch } = props;
8
+ const { t } = useTranslation();
9
+ return (_jsxs("div", { className: "px-3 py-2 flex flex-row items-center gap-2 border-b border-gray-200", children: [_jsxs("div", { className: "flex flex-row items-center gap-2", children: [_jsx(CannedResponseIcon, { size: 20, className: "text-blue-500" }), _jsx("span", { className: "text-base font-medium", children: t("canned_responses") })] }), _jsxs("div", { className: "flex flex-1 flex-row items-center justify-end gap-2", children: [_jsx(Input, { size: "large", placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 20, className: "text-gray-500" }), style: { width: 260 }, className: "text-sm", value: search, onChange: (e) => setSearch === null || setSearch === void 0 ? void 0 : setSearch(e.target.value) }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-o", size: 16 }) })] })] }));
10
+ };
11
+ export default CannedResponseHeader;
@@ -0,0 +1,8 @@
1
+ interface ICannedResponseProps {
2
+ onClose?: () => void;
3
+ openCreateCannedModal?: () => void;
4
+ cannedQuery?: string;
5
+ }
6
+ declare const CannedResponse: ({ onClose, openCreateCannedModal, cannedQuery, }: ICannedResponseProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default CannedResponse;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/index.tsx"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,cAAc,GAAI,kDAIrB,oBAAoB,4CAgDtB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useDebounce } from "ahooks";
3
+ import CannedResponseBody from "./CannedResponseBody";
4
+ import CannedResponseHeader from "./CannedResponseHeader";
5
+ import { useCallback, useState } from "react";
6
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
7
+ import { $getRoot } from "lexical";
8
+ import { $generateNodesFromDOM } from "@lexical/html";
9
+ import CannedResponseFooter from "./CannedResponseFooter";
10
+ const CannedResponse = ({ onClose, openCreateCannedModal, cannedQuery = "", }) => {
11
+ const [editor] = useLexicalComposerContext();
12
+ const [search, setSearch] = useState("");
13
+ const debouncedSearch = useDebounce(search, {
14
+ wait: 300,
15
+ });
16
+ const onSelectCannedResponse = useCallback((content) => {
17
+ editor.update(() => {
18
+ const root = $getRoot();
19
+ // 1. Clear content cũ
20
+ root.clear();
21
+ // 2. Parse HTML -> DOM
22
+ const parser = new DOMParser();
23
+ const dom = parser.parseFromString(content, "text/html");
24
+ // 3. Convert DOM -> Lexical nodes
25
+ const nodes = $generateNodesFromDOM(editor, dom);
26
+ // 4. Append vào editor
27
+ root.append(...nodes);
28
+ // 5. Move cursor về cuối (optional nhưng nên có)
29
+ root.selectEnd();
30
+ });
31
+ }, [editor]);
32
+ return (_jsxs("div", { className: "w-[600px]", children: [_jsx(CannedResponseHeader, { onClose: onClose, search: search, setSearch: setSearch }), _jsx(CannedResponseBody, { search: debouncedSearch, onSelectCannedResponse: onSelectCannedResponse, cannedQuery: cannedQuery }), _jsx(CannedResponseFooter, { openCreateCannedModal: openCreateCannedModal })] }));
33
+ };
34
+ export default CannedResponse;
@@ -0,0 +1,11 @@
1
+ import { IMyTeamResponse } from "../../../types/dto";
2
+ interface TeamItemProps {
3
+ team: IMyTeamResponse;
4
+ onSelectCategory?: (teamId: string, categoryId?: string) => void;
5
+ selectedTeamId?: string;
6
+ selectedCategoryId?: string;
7
+ defaultOpen?: boolean;
8
+ }
9
+ declare const TeamItem: ({ team, onSelectCategory, selectedTeamId, selectedCategoryId, defaultOpen, }: TeamItemProps) => import("react/jsx-runtime").JSX.Element;
10
+ export default TeamItem;
11
+ //# sourceMappingURL=TeamItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamItem.d.ts","sourceRoot":"","sources":["../../../../src/components/cannedResponse/team/TeamItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQrD,UAAU,aAAa;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,QAAQ,GAAI,8EAMf,aAAa,4CAiFf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useFetchCannedCategories } from "../../../hooks/cannedResponse/useFetchCannedCategories";
3
+ import { useBoolean } from "ahooks";
4
+ import { Icon } from "../../icon";
5
+ import clsx from "clsx";
6
+ import { useTranslation } from "react-i18next";
7
+ import { useCallback } from "react";
8
+ const TeamItem = ({ team, onSelectCategory, selectedTeamId, selectedCategoryId, defaultOpen = false, }) => {
9
+ const { t } = useTranslation();
10
+ const [isOpen, { toggle }] = useBoolean(defaultOpen);
11
+ const { data: categories } = useFetchCannedCategories(team.teamId, isOpen);
12
+ const onToggle = useCallback(() => {
13
+ if (!isOpen && !selectedTeamId && !!(team === null || team === void 0 ? void 0 : team.teamId)) {
14
+ onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, undefined);
15
+ }
16
+ toggle();
17
+ }, [toggle, isOpen, onSelectCategory, team === null || team === void 0 ? void 0 : team.teamId, selectedTeamId]);
18
+ return (_jsxs("div", { className: "border-b border-gray-200", children: [_jsxs("div", { className: "flex items-center justify-between py-2 cursor-pointer", onClick: onToggle, children: [_jsx("span", { className: "text-sm font-medium truncate flex-1", children: (team === null || team === void 0 ? void 0 : team.name) || "" }), _jsx(Icon, { icon: isOpen ? "angle-up-b" : "angle-down-b", size: 16, className: clsx(!isOpen && "text-gray-500") })] }), isOpen && (_jsxs("div", { className: "mb-2", children: [_jsx("div", { className: clsx("px-3 py-1 cursor-pointer hover:bg-gray-100 rounded-md", selectedTeamId &&
19
+ selectedTeamId === (team === null || team === void 0 ? void 0 : team.teamId) &&
20
+ !selectedCategoryId &&
21
+ "bg-gray-100"), onClick: () => onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, undefined), children: _jsx("span", { className: clsx("text-sm truncate", selectedTeamId === (team === null || team === void 0 ? void 0 : team.teamId) && !selectedCategoryId
22
+ ? "font-medium text-blue-500"
23
+ : "text-gray-500"), children: t("all") }) }, `${team.teamId}-all`), categories === null || categories === void 0 ? void 0 : categories.map((category) => {
24
+ const isSelected = selectedTeamId &&
25
+ selectedCategoryId &&
26
+ selectedTeamId === (category === null || category === void 0 ? void 0 : category.teamId) &&
27
+ selectedCategoryId === (category === null || category === void 0 ? void 0 : category.id);
28
+ return (_jsx("div", { className: clsx("px-3 py-1 cursor-pointer hover:bg-gray-100 rounded-md", isSelected && "bg-gray-100"), onClick: () => onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, category === null || category === void 0 ? void 0 : category.id), children: _jsx("span", { className: clsx("text-sm truncate", isSelected ? "font-medium text-blue-500" : "text-gray-500"), children: category === null || category === void 0 ? void 0 : category.name }) }, `${team.teamId}-${category.id}`));
29
+ })] }))] }));
30
+ };
31
+ export default TeamItem;
@@ -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,mDA0FhC,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;AAQpD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,kBAEhC,8BAA8B,mDA2FhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -10,8 +10,12 @@ import { formatTimestamp, parseLatestMessage } from "../../utils/common";
10
10
  const ConversationBySessionItem = ({ sessionItem, }) => {
11
11
  var _a;
12
12
  const { t } = useTranslation();
13
- const { user } = useChatContext();
14
- const isSelected = useConversationStore((state) => { var _a; return state.selectedConversationId === ((_a = sessionItem === null || sessionItem === void 0 ? void 0 : sessionItem.conversation) === null || _a === void 0 ? void 0 : _a.conversationID); });
13
+ const user = useChatContext().user;
14
+ const isSelected = useConversationStore((state) => {
15
+ var _a;
16
+ return state.selectedConversationId ===
17
+ ((_a = sessionItem === null || sessionItem === void 0 ? void 0 : sessionItem.conversation) === null || _a === void 0 ? void 0 : _a.conversationID);
18
+ });
15
19
  const conversation = sessionItem === null || sessionItem === void 0 ? void 0 : sessionItem.conversation;
16
20
  const router = useRouter();
17
21
  const pathname = usePathname();
@@ -73,7 +73,7 @@ const LinkCollection = ({ onClose }) => {
73
73
  const menuItems = [
74
74
  {
75
75
  key: "open",
76
- label: "Xem tin nhắn",
76
+ label: t("view_message"),
77
77
  onClick: () => onPressItem(item.chatLog),
78
78
  },
79
79
  ];
@@ -0,0 +1,7 @@
1
+ import { MessageItem } from "@openim/wasm-client-sdk";
2
+ interface MediaPreviewIconProps {
3
+ message?: MessageItem;
4
+ }
5
+ declare const MediaPreviewIcon: ({ message }: MediaPreviewIconProps) => import("react/jsx-runtime").JSX.Element | null;
6
+ export default MediaPreviewIcon;
7
+ //# sourceMappingURL=MediaPreviewIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaPreviewIcon.d.ts","sourceRoot":"","sources":["../../../src/components/message/MediaPreviewIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAInE,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,QAAA,MAAM,gBAAgB,GAAI,aAAa,qBAAqB,mDA8B3D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MessageType } from "@openim/wasm-client-sdk";
3
+ import { Image } from "antd";
4
+ import { documentIcon } from "../../assets/svg";
5
+ const MediaPreviewIcon = ({ message }) => {
6
+ var _a, _b;
7
+ if (!message) {
8
+ return null;
9
+ }
10
+ if (message.contentType === MessageType.FileMessage) {
11
+ return _jsx("div", { children: documentIcon });
12
+ }
13
+ if (message.contentType === MessageType.PictureMessage) {
14
+ const imageUrl = ((_a = message.pictureElem.sourcePicture) === null || _a === void 0 ? void 0 : _a.url) ||
15
+ ((_b = message.pictureElem.snapshotPicture) === null || _b === void 0 ? void 0 : _b.url);
16
+ return (_jsx("div", { className: "w-10 h-10", children: _jsx(Image, { rootClassName: "message-image cursor-pointer", className: "rounded-md", src: imageUrl }) }));
17
+ }
18
+ if (message.contentType === MessageType.VideoMessage) {
19
+ const videoUrl = message.videoElem.videoUrl;
20
+ return (_jsx("div", { className: "flex flex-col justify-center items-center w-10 h-10 bg-black rounded-md", children: _jsx("video", { src: videoUrl, controls: false, autoPlay: false, muted: true }) }));
21
+ }
22
+ return null;
23
+ };
24
+ export default MediaPreviewIcon;
@@ -122,6 +122,6 @@ const MessageHeader = ({ onClose, currentSession }) => {
122
122
  setCurrentSessionStatus(currentSession.status);
123
123
  }
124
124
  }, [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: "2 thành viên" })] }), _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 }) }))] })] }));
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 }) }))] })] }));
126
126
  };
127
127
  export default MessageHeader;
@@ -3,6 +3,7 @@ interface MessageListProps {
3
3
  searchClientMsgID?: string;
4
4
  className?: string;
5
5
  onClose?: () => void;
6
+ openCreateCannedModal?: () => void;
6
7
  }
7
8
  declare const MessageList: (props: MessageListProps) => import("react/jsx-runtime").JSX.Element;
8
9
  export default MessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAwBA,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;CACtB;AAGD,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAqN3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
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,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMessage } from "../../hooks/message/useMessage";
4
- import { Empty, Spin } from "antd";
5
- import { useEffect, useMemo, useRef } from "react";
4
+ import { Empty, Modal, Spin } from "antd";
5
+ import { useCallback, useEffect, useMemo, 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";
@@ -14,26 +14,50 @@ import { images } from "../../constants/images";
14
14
  import { useTranslation } from "react-i18next";
15
15
  import useConversationStore from "../../store/conversation";
16
16
  import { isNumber } from "lodash";
17
- import { useDebounceFn } from "ahooks";
17
+ 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
21
  import { useGetSession } from "../../hooks/session/useGetSession";
22
+ import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
22
23
  dayjs.extend(isToday);
23
24
  const BOTTOM_THRESHOLD = -5;
24
25
  const MessageList = (props) => {
25
26
  var _a;
26
27
  const { t } = useTranslation();
27
28
  const { user } = useChatContext();
28
- const { onClose, conversationId, searchClientMsgID } = props;
29
+ const { onClose, conversationId, searchClientMsgID, openCreateCannedModal } = props;
29
30
  const scrollRef = useRef(null);
30
31
  const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useMessage(conversationId, searchClientMsgID);
31
32
  const conversationData = useConversationStore((state) => state.conversationData);
33
+ const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
34
+ const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
32
35
  const { dataFlatten: sessions, refetch: refetchSession } = useGetSession({
33
36
  filter: {
34
37
  conversationIds: !!conversationId ? [conversationId] : [],
35
38
  },
36
39
  });
40
+ const [openMenuId, setOpenMenuId] = useState(null);
41
+ const [selectedItem, setSelectedItem] = useState(null);
42
+ const [showConfirmRevoke, { setTrue: openConfirmRevoke, setFalse: closeConfirmRevoke },] = useBoolean(false);
43
+ const { revokeMessage, loading: isRevoking } = useRevokeMessage();
44
+ const handleOpenRevoke = useCallback((clientMsgID) => {
45
+ setSelectedItem(clientMsgID);
46
+ openConfirmRevoke();
47
+ }, [openConfirmRevoke]);
48
+ const handleCloseRevoke = useCallback(() => {
49
+ setSelectedItem(null);
50
+ closeConfirmRevoke();
51
+ }, [closeConfirmRevoke]);
52
+ const onRevokeMessage = useCallback(async () => {
53
+ await revokeMessage((conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "", selectedItem || "");
54
+ handleCloseRevoke();
55
+ }, [
56
+ conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID,
57
+ selectedItem,
58
+ revokeMessage,
59
+ handleCloseRevoke,
60
+ ]);
37
61
  const currentSession = useMemo(() => {
38
62
  return sessions === null || sessions === void 0 ? void 0 : sessions.find((session) => session.conversationId === conversationId);
39
63
  }, [sessions, conversationId]);
@@ -84,6 +108,15 @@ const MessageList = (props) => {
84
108
  }, 500);
85
109
  }, 200);
86
110
  };
111
+ const onPressQuoteMessage = useCallback((clientMsgID) => {
112
+ const hasMessage = loadState.messageList.some((msg) => msg.clientMsgID === clientMsgID);
113
+ if (hasMessage) {
114
+ scrollToMessage(clientMsgID);
115
+ }
116
+ else {
117
+ setSearchClientMsgID(clientMsgID);
118
+ }
119
+ }, [scrollToMessage, loadState.messageList, setSearchClientMsgID]);
87
120
  const loadMoreOldMessage = () => {
88
121
  if (!loadState.hasMoreOld || moreOldLoading)
89
122
  return;
@@ -123,6 +156,16 @@ const MessageList = (props) => {
123
156
  loadState.initLoading,
124
157
  handleMarkConversationMessageAsRead,
125
158
  ]);
159
+ useEffect(() => {
160
+ if (!openMenuId)
161
+ return;
162
+ const scrollContainer = scrollRef.current;
163
+ if (!scrollContainer)
164
+ return;
165
+ const close = () => setOpenMenuId(null);
166
+ scrollContainer.addEventListener("scroll", close);
167
+ return () => scrollContainer.removeEventListener("scroll", close);
168
+ }, [openMenuId]);
126
169
  if (!conversationData) {
127
170
  return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: _jsx(Empty, { description: t("no_conversation_data") }) }));
128
171
  }
@@ -142,13 +185,13 @@ const MessageList = (props) => {
142
185
  display: "flex",
143
186
  flexDirection: "column-reverse",
144
187
  minWidth: 0,
145
- width: "100%"
188
+ width: "100%",
146
189
  }, inverse: true, hasMore: loadState.hasMoreOld, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableMessagesDiv", onScroll: (e) => {
147
190
  const target = e.target;
148
191
  if (target.scrollTop > BOTTOM_THRESHOLD) {
149
192
  handleMarkConversationMessageAsRead();
150
193
  loadMoreNewMessage();
151
194
  }
152
- }, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array }, message.clientMsgID))) }) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), _jsx(MessageFooter, { currentSession: currentSession })] }));
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") }) })] }));
153
196
  };
154
197
  export default MessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAoFA,QAAA,MAAM,SAAS,+CA0Od,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,+CA0Gd,CAAC;AAEF,eAAe,SAAS,CAAC"}