@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
@@ -1,9 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from "react";
4
- import { Button, Upload, message, } from "antd";
5
- import clsx from "clsx";
6
- import EmojiPicker from "./EmojiPicker";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { Button } from "antd";
7
5
  import { Icon } from "../../icon";
8
6
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
9
7
  import { $createParagraphNode, $getRoot, $getSelection, $isRangeSelection, } from "lexical";
@@ -11,33 +9,27 @@ import { $generateHtmlFromNodes } from "@lexical/html";
11
9
  import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
12
10
  import { $isQuoteNode } from "@lexical/rich-text";
13
11
  import { useMessageFooterContext } from ".";
14
- import { useTranslation } from "react-i18next";
15
- import { validateFile, validateVideoLimit } from "../../../utils/fileValidation";
16
- const documentTypes = [
17
- "application/pdf",
18
- "application/msword",
19
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
20
- ];
21
- const EmojiIcon = (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997Z", fill: "white" }), _jsx("path", { d: "M7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", fill: "white" }), _jsx("path", { d: "M7.59997 12.4C7.59997 12.4 8.49997 13.2 9.99997 13.2C11.5 13.2 12.4 12.4 12.4 12.4M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997ZM18 9.99997C18 14.4182 14.4182 18 9.99997 18C5.58169 18 1.99997 14.4182 1.99997 9.99997C1.99997 5.58169 5.58169 1.99997 9.99997 1.99997C14.4182 1.99997 18 5.58169 18 9.99997ZM7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }));
22
- const AttachIcon = (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M15.6 10.0001V11.2001C15.6 14.5137 12.9137 17.2001 9.59998 17.2001C6.28626 17.2001 3.59998 14.5137 3.59998 11.2001V6.79999C3.59998 4.59085 5.39084 2.79999 7.59998 2.79999C9.8091 2.79999 11.6 4.59085 11.6 6.79999V11.2C11.6 12.3045 10.7045 13.2 9.59998 13.2C8.49542 13.2 7.59998 12.3045 7.59998 11.2V7.99999", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
12
+ import MediaActions from "./MediaActions";
13
+ import useConversationStore from "../../../store/conversation";
23
14
  const ActionBar = () => {
24
15
  const [editor] = useLexicalComposerContext();
25
- const { onSendMessage, setListUploadFiles } = useMessageFooterContext();
26
- const containerRef = useRef(null);
27
- const [showEmojiPicker, setShowEmojiPicker] = useState(false);
28
- const { listUploadFiles } = useMessageFooterContext();
16
+ const { onSendMessage, listUploadFiles, setListUploadFiles } = useMessageFooterContext();
17
+ const quotedMessage = useConversationStore((state) => state.quotedMessage);
29
18
  const [isEmptyInput, setIsEmptyInput] = useState(true);
30
- const { t } = useTranslation();
31
- const canSend = !isEmptyInput || listUploadFiles.length > 0;
19
+ const canSend = useMemo(() => {
20
+ const hasQuote = !!(quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID);
21
+ return hasQuote
22
+ ? !isEmptyInput
23
+ : !isEmptyInput || listUploadFiles.length > 0;
24
+ }, [isEmptyInput, listUploadFiles === null || listUploadFiles === void 0 ? void 0 : listUploadFiles.length, quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID]);
32
25
  const handleSend = useCallback(() => {
33
26
  let plainText = "";
34
27
  let richText = "";
35
- // lấy plain text & html
36
28
  editor.getEditorState().read(() => {
37
29
  plainText = $getRoot().getTextContent();
38
30
  richText = $generateHtmlFromNodes(editor);
39
31
  });
40
- if (plainText.trim().length > 0 || listUploadFiles.length > 0) {
32
+ if (canSend) {
41
33
  onSendMessage({
42
34
  plainText,
43
35
  richText,
@@ -67,9 +59,7 @@ const ActionBar = () => {
67
59
  selection.formatText("strikethrough");
68
60
  }
69
61
  if (isQuoteNode) {
70
- // Nếu đang là QuoteNode → chuyển về Paragraph (bình thường)
71
62
  const paragraph = $createParagraphNode();
72
- // copy con của quote sang paragraph
73
63
  const children = topLevelNode.getChildren();
74
64
  children.forEach((child) => paragraph.append(child));
75
65
  topLevelNode.replace(paragraph);
@@ -81,68 +71,7 @@ const ActionBar = () => {
81
71
  }
82
72
  }
83
73
  });
84
- }, [editor, onSendMessage]);
85
- const handleEmojiSelect = useCallback((emoji) => {
86
- editor.update(() => {
87
- const selection = $getSelection();
88
- if ($isRangeSelection(selection)) {
89
- selection.insertText(emoji); // chèn emoji như text bình thường
90
- }
91
- });
92
- setShowEmojiPicker(false);
93
- }, [editor]);
94
- const beforeUploadImagesAndVideo = (file, fileList) => {
95
- // Validate file type and size
96
- const validation = validateFile(file, t);
97
- if (!validation.isValid) {
98
- message.error(validation.error);
99
- return Upload.LIST_IGNORE;
100
- }
101
- // Validate video limit
102
- const videoValidation = validateVideoLimit([file], listUploadFiles, t);
103
- if (!videoValidation.isValid) {
104
- message.error(videoValidation.error);
105
- return Upload.LIST_IGNORE;
106
- }
107
- // Check if multiple videos in current upload batch
108
- const newVideos = fileList.filter((f) => { var _a; return (_a = f.type) === null || _a === void 0 ? void 0 : _a.startsWith("video/"); });
109
- if (newVideos.length > 1) {
110
- message.error(t("video_limit_exceeded", {
111
- defaultValue: "Chỉ được phép tải lên 1 video duy nhất",
112
- }));
113
- return Upload.LIST_IGNORE;
114
- }
115
- return false;
116
- };
117
- const beforeUploadFile = (file) => {
118
- const isAllowed = documentTypes.includes(file.type);
119
- if (!isAllowed) {
120
- message.error(`${file.name} không đúng định dạng (chỉ hỗ trợ PDF, DOC, DOCX)`);
121
- return Upload.LIST_IGNORE;
122
- }
123
- return false;
124
- };
125
- const handleChange = (info) => {
126
- var _a;
127
- let newList = [...info.fileList];
128
- const lastFile = info.file;
129
- if (documentTypes.includes(lastFile.type || "")) {
130
- newList = newList.filter((f) => !documentTypes.includes(f.type || ""));
131
- const originFile = ((_a = lastFile.originFileObj) !== null && _a !== void 0 ? _a : lastFile);
132
- newList.push(Object.assign(Object.assign({}, lastFile), { originFileObj: originFile }));
133
- }
134
- setListUploadFiles(newList);
135
- };
136
- useEffect(() => {
137
- const handleClickOutside = (event) => {
138
- if (containerRef.current &&
139
- !containerRef.current.contains(event.target)) {
140
- setShowEmojiPicker(false);
141
- }
142
- };
143
- document.addEventListener("mousedown", handleClickOutside);
144
- return () => document.removeEventListener("mousedown", handleClickOutside);
145
- }, []);
74
+ }, [editor, onSendMessage, canSend]);
146
75
  useEffect(() => {
147
76
  return editor.registerUpdateListener(({ editorState }) => {
148
77
  editorState.read(() => {
@@ -152,6 +81,6 @@ const ActionBar = () => {
152
81
  });
153
82
  });
154
83
  }, [editor]);
155
- return (_jsxs("div", { className: "flex items-center justify-between px-4", ref: containerRef, children: [_jsxs("div", { className: "flex items-center gap-3 relative", children: [_jsx(Button, { type: "text", shape: "default", className: clsx("text-gray-500 w-8 h-8 p-0", showEmojiPicker ? "bg-blue-100 text-blue-600" : "text-gray-500"), onClick: () => setShowEmojiPicker(!showEmojiPicker), children: EmojiIcon }), _jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0 text-gray-500", children: _jsx(Icon, { icon: "image-02-o", size: 22 }) }) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0 text-gray-500", children: AttachIcon }) }), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: handleSend, disabled: !canSend, children: _jsx(Icon, { icon: "send-b", size: 28, className: `${canSend ? "text-blue-500" : "text-gray-400"}` }) })] }));
84
+ return (_jsxs("div", { className: "flex items-center justify-between px-4", children: [_jsx(MediaActions, { listUploadFiles: listUploadFiles, onFilesChange: setListUploadFiles, buttonClassName: "text-gray-500 w-8 h-8 p-0" }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: handleSend, disabled: !canSend, children: _jsx(Icon, { icon: "send-b", size: 28, className: `${canSend ? "text-blue-500" : "text-gray-400"}` }) })] }));
156
85
  };
157
86
  export default ActionBar;
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ onOpen: (query: string) => void;
3
+ onClose: () => void;
4
+ };
5
+ export declare function CannedResponseTriggerPlugin({ onOpen, onClose }: Props): null;
6
+ export {};
7
+ //# sourceMappingURL=CannedResponsePlugin.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ import { useEffect } from "react";
2
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
+ import { $getSelection, $isRangeSelection } from "lexical";
4
+ export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
5
+ const [editor] = useLexicalComposerContext();
6
+ useEffect(() => {
7
+ return editor.registerUpdateListener(({ editorState }) => {
8
+ editorState.read(() => {
9
+ const selection = $getSelection();
10
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
11
+ onClose();
12
+ return;
13
+ }
14
+ const anchor = selection.anchor;
15
+ const node = anchor.getNode();
16
+ const text = node.getTextContent();
17
+ const offset = anchor.offset;
18
+ const before = text.slice(0, offset);
19
+ // match #abc (KHÔNG có space phía sau)
20
+ const match = before.match(/(^|\s)#(\w*)$/);
21
+ if (match) {
22
+ onOpen(match[2] || "");
23
+ }
24
+ else {
25
+ onClose();
26
+ }
27
+ });
28
+ });
29
+ }, [editor, onOpen, onClose]);
30
+ return null;
31
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EmojiPicker.tsx"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,QAAA,MAAM,WAAW,GAAI,4BAA4B,gBAAgB,4CAyLhE,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EmojiPicker.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,4BAA4B,gBAAgB,4CAiMhE,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,11 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
+ import { useTranslation } from "react-i18next";
4
5
  const EmojiPicker = ({ onEmojiSelect, onClose }) => {
6
+ const { t } = useTranslation("common");
5
7
  const [activeCategory, setActiveCategory] = useState("smileys");
6
8
  const emojiCategories = {
7
9
  smileys: {
8
- name: "Smileys & People",
10
+ name: t("emoji_smileys"),
9
11
  emojis: [
10
12
  "😀",
11
13
  "😃",
@@ -41,7 +43,7 @@ const EmojiPicker = ({ onEmojiSelect, onClose }) => {
41
43
  ],
42
44
  },
43
45
  nature: {
44
- name: "Animals & Nature",
46
+ name: t("emoji_nature"),
45
47
  emojis: [
46
48
  "🐶",
47
49
  "🐱",
@@ -76,7 +78,7 @@ const EmojiPicker = ({ onEmojiSelect, onClose }) => {
76
78
  ],
77
79
  },
78
80
  food: {
79
- name: "Food & Drink",
81
+ name: t("emoji_food"),
80
82
  emojis: [
81
83
  "🍎",
82
84
  "🍐",
@@ -111,7 +113,7 @@ const EmojiPicker = ({ onEmojiSelect, onClose }) => {
111
113
  ],
112
114
  },
113
115
  activities: {
114
- name: "Activities",
116
+ name: t("emoji_activities"),
115
117
  emojis: [
116
118
  "⚽",
117
119
  "🏀",
@@ -146,6 +148,8 @@ const EmojiPicker = ({ onEmojiSelect, onClose }) => {
146
148
  ],
147
149
  },
148
150
  };
149
- return (_jsx("div", { className: "absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-xl shadow-lg w-80 z-50", children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: "Choose an emoji" }), _jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 text-xl", children: "\u00D7" })] }), _jsx("div", { className: "flex gap-1 mb-3", children: Object.entries(emojiCategories).map(([key, category]) => (_jsx("button", { onClick: () => setActiveCategory(key), className: `px-3 py-1 text-xs rounded-full transition-colors ${activeCategory === key ? "bg-blue-100 text-blue-600" : "text-gray-500 hover:text-gray-700"}`, children: category.name.split(" ")[0] }, key))) }), _jsx("div", { className: "grid grid-cols-8 gap-1 max-h-48 overflow-y-auto", children: emojiCategories[activeCategory].emojis.map((emoji, index) => (_jsx("button", { onClick: () => onEmojiSelect(emoji), className: "p-2 text-xl hover:bg-gray-100 rounded-lg transition-colors", children: emoji }, index))) })] }) }));
151
+ return (_jsx("div", { className: "absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-xl shadow-lg w-96 z-50", children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: t("choose_emoji") }), _jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 text-xl", children: "\u00D7" })] }), _jsx("div", { className: "flex gap-1 mb-3", children: Object.entries(emojiCategories).map(([key, category]) => (_jsx("button", { onClick: () => setActiveCategory(key), className: `px-3 py-1 text-xs rounded-full transition-colors ${activeCategory === key
152
+ ? "bg-blue-100 text-blue-600"
153
+ : "text-gray-500 hover:text-gray-700"}`, children: category.name }, key))) }), _jsx("div", { className: "grid grid-cols-8 gap-1 max-h-48 overflow-y-auto", children: emojiCategories[activeCategory].emojis.map((emoji, index) => (_jsx("button", { onClick: () => onEmojiSelect(emoji), className: "p-2 text-xl hover:bg-gray-100 rounded-lg transition-colors", children: emoji }, index))) })] }) }));
150
154
  };
151
155
  export default EmojiPicker;
@@ -1 +1 @@
1
- {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAsBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAwFnC"}
1
+ {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAmGnC"}
@@ -6,11 +6,12 @@ import { $generateHtmlFromNodes } from "@lexical/html";
6
6
  import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
7
7
  import { $isQuoteNode } from "@lexical/rich-text";
8
8
  import { useMessageFooterContext } from ".";
9
+ import useConversationStore from "../../../store/conversation";
9
10
  export default function EnterHandler() {
10
11
  const [editor] = useLexicalComposerContext();
11
- const { onSendMessage } = useMessageFooterContext();
12
+ const { onSendMessage, listUploadFiles } = useMessageFooterContext();
12
13
  const shiftEnterCount = useRef(0);
13
- const { listUploadFiles } = useMessageFooterContext();
14
+ const quotedMessage = useConversationStore((state) => state.quotedMessage);
14
15
  useEffect(() => {
15
16
  return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
16
17
  // Case 1: Enter (submit, chặn xuống dòng)
@@ -24,11 +25,15 @@ export default function EnterHandler() {
24
25
  plainText = $getRoot().getTextContent();
25
26
  richText = $generateHtmlFromNodes(editor);
26
27
  });
27
- if (plainText.trim().length > 0 || listUploadFiles.length > 0) {
28
+ const hasText = plainText.trim().length > 0;
29
+ const hasFiles = listUploadFiles.length > 0;
30
+ const hasQuote = !!(quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID);
31
+ const canSend = hasQuote ? hasText : hasText || hasFiles;
32
+ if (canSend) {
28
33
  onSendMessage({
29
34
  plainText,
30
35
  richText,
31
- type: listUploadFiles.length > 0 ? "file" : "text",
36
+ type: hasFiles ? "file" : "text",
32
37
  });
33
38
  }
34
39
  editor.update(() => {
@@ -74,6 +79,12 @@ export default function EnterHandler() {
74
79
  }
75
80
  return false;
76
81
  }, COMMAND_PRIORITY_NORMAL);
77
- }, [editor, onSendMessage, listUploadFiles]);
82
+ }, [
83
+ editor,
84
+ onSendMessage,
85
+ listUploadFiles,
86
+ quotedMessage,
87
+ quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID,
88
+ ]);
78
89
  return null;
79
90
  }
@@ -1,9 +1,14 @@
1
+ import { UploadFile } from "antd";
1
2
  interface ShortenOptions {
2
3
  maxLength?: number;
3
4
  keepStart?: number;
4
5
  keepEnd?: number;
5
6
  }
6
7
  export declare const shortenFileName: (name: string, options?: ShortenOptions) => string;
8
+ export declare function FilePreviewList({ files, onRemove, }: {
9
+ files: UploadFile[];
10
+ onRemove: (file: UploadFile) => void;
11
+ }): import("react/jsx-runtime").JSX.Element | null;
7
12
  declare const FilePreview: () => import("react/jsx-runtime").JSX.Element;
8
13
  export default FilePreview;
9
14
  //# sourceMappingURL=FilePreview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/FilePreview.tsx"],"names":[],"mappings":"AAYA,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,UAAS,cAAmB,WAkBzE,CAAC;AAEF,QAAA,MAAM,WAAW,+CAkFhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"FilePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/FilePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAS1C,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,UAAS,cAAmB,WAiBzE,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACtC,kDA8EA;AAED,QAAA,MAAM,WAAW,+CAQhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -17,20 +17,14 @@ export const shortenFileName = (name, options = {}) => {
17
17
  const dotIndex = name.lastIndexOf(".");
18
18
  const ext = dotIndex !== -1 ? name.slice(dotIndex) : "";
19
19
  const base = dotIndex !== -1 ? name.slice(0, dotIndex) : name;
20
- // Tính toán độ dài phần đầu & cuối
21
- const available = maxLength - ext.length - 3; // trừ "...”
20
+ const available = maxLength - ext.length - 3;
22
21
  const startLen = keepStart !== null && keepStart !== void 0 ? keepStart : Math.floor(available / 2);
23
22
  const endLen = keepEnd !== null && keepEnd !== void 0 ? keepEnd : Math.floor(available / 2);
24
23
  const start = base.slice(0, startLen);
25
24
  const end = base.slice(-endLen);
26
25
  return `${start}...${end}${ext}`;
27
26
  };
28
- const FilePreview = () => {
29
- var _a;
30
- const { listUploadFiles, setListUploadFiles } = useMessageFooterContext();
31
- const onRemoveFile = (file) => {
32
- setListUploadFiles(listUploadFiles.filter((f) => f.uid !== file.uid));
33
- };
27
+ export function FilePreviewList({ files, onRemove, }) {
34
28
  const renderFilePreview = useCallback((file) => {
35
29
  var _a, _b, _c;
36
30
  const isDocument = documentTypes.includes(((_a = file === null || file === void 0 ? void 0 : file.originFileObj) === null || _a === void 0 ? void 0 : _a.type) || "");
@@ -40,10 +34,19 @@ const FilePreview = () => {
40
34
  src = URL.createObjectURL(file.originFileObj);
41
35
  }
42
36
  if (isDocument) {
43
- return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2", children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(((_c = file === null || file === void 0 ? void 0 : file.originFileObj) === null || _c === void 0 ? void 0 : _c.name) || "") }), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemoveFile(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) })] }, file.uid));
37
+ return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2", children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(((_c = file === null || file === void 0 ? void 0 : file.originFileObj) === null || _c === void 0 ? void 0 : _c.name) || "") }), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemove(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) })] }, file.uid));
44
38
  }
45
- return (_jsxs("div", { className: "relative rounded-md border", children: [isVideo ? (_jsx("video", { src: src, className: "w-[48px] h-[48px] object-cover rounded-lg", autoPlay: false })) : (_jsx("img", { src: src, alt: file.name, className: "w-[48px] h-[48px] object-cover rounded-lg" })), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemoveFile(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) }), isVideo && (_jsx(Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", icon: "play-b", size: 20, color: "white" }))] }, file.uid));
46
- }, [listUploadFiles]);
47
- return (_jsx("div", { className: "overflow-x-auto mb-[-4px]", children: _jsx("div", { className: "border-b py-2 px-4", children: _jsx("div", { className: "flex items-center gap-2", children: (_a = listUploadFiles === null || listUploadFiles === void 0 ? void 0 : listUploadFiles.map) === null || _a === void 0 ? void 0 : _a.call(listUploadFiles, (file) => renderFilePreview(file)) }) }) }));
39
+ return (_jsxs("div", { className: "relative rounded-md border", children: [isVideo ? (_jsx("video", { src: src, className: "w-[48px] h-[48px] object-cover rounded-lg", autoPlay: false })) : (_jsx("img", { src: src, alt: file.name, className: "w-[48px] h-[48px] object-cover rounded-lg" })), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemove(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) }), isVideo && (_jsx(Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", icon: "play-b", size: 20, color: "white" }))] }, file.uid));
40
+ }, [files]);
41
+ if (files.length === 0)
42
+ return null;
43
+ return (_jsx("div", { className: "overflow-x-auto mb-[-4px]", children: _jsx("div", { className: "border-b py-2 px-4", children: _jsx("div", { className: "flex items-center gap-2", children: files.map((file) => renderFilePreview(file)) }) }) }));
44
+ }
45
+ const FilePreview = () => {
46
+ const { listUploadFiles, setListUploadFiles } = useMessageFooterContext();
47
+ const onRemoveFile = (file) => {
48
+ setListUploadFiles(listUploadFiles.filter((f) => f.uid !== file.uid));
49
+ };
50
+ return _jsx(FilePreviewList, { files: listUploadFiles, onRemove: onRemoveFile });
48
51
  };
49
52
  export default FilePreview;
@@ -0,0 +1,10 @@
1
+ import { UploadFile } from "antd";
2
+ interface MediaActionsProps {
3
+ listUploadFiles: UploadFile[];
4
+ onFilesChange: (files: UploadFile[]) => void;
5
+ buttonClassName?: string;
6
+ showMediaUpload?: boolean;
7
+ }
8
+ export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload, }: MediaActionsProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=MediaActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAoD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,GACvB,EAAE,iBAAiB,2CAwKnB"}
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
5
+ import { $getSelection, $isRangeSelection } from "lexical";
6
+ import { Button, Upload, message as antdMessage } from "antd";
7
+ import clsx from "clsx";
8
+ import { useTranslation } from "react-i18next";
9
+ import EmojiPicker from "./EmojiPicker";
10
+ import { Icon } from "../../icon";
11
+ import { validateFile, validateVideoLimit, } from "../../../utils/fileValidation";
12
+ import { DOCUMENT_TYPES } from "./editorConfig";
13
+ import CannedResponseIcon from "../../../assets/svg/cannedResponse";
14
+ import { useMessageFooterContext } from ".";
15
+ import useAuthStore from "../../../store/auth";
16
+ const EmojiIcon = (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M7.59997 12.4C7.59997 12.4 8.49997 13.2 9.99997 13.2C11.5 13.2 12.4 12.4 12.4 12.4M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997ZM18 9.99997C18 14.4182 14.4182 18 9.99997 18C5.58169 18 1.99997 14.4182 1.99997 9.99997C1.99997 5.58169 5.58169 1.99997 9.99997 1.99997C14.4182 1.99997 18 5.58169 18 9.99997ZM7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
17
+ const AttachIcon = (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M15.6 10.0001V11.2001C15.6 14.5137 12.9137 17.2001 9.59998 17.2001C6.28626 17.2001 3.59998 14.5137 3.59998 11.2001V6.79999C3.59998 4.59085 5.39084 2.79999 7.59998 2.79999C9.8091 2.79999 11.6 4.59085 11.6 6.79999V11.2C11.6 12.3045 10.7045 13.2 9.59998 13.2C8.49542 13.2 7.59998 12.3045 7.59998 11.2V7.99999", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
18
+ export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload = true, }) {
19
+ const [editor] = useLexicalComposerContext();
20
+ const { t } = useTranslation();
21
+ const [showEmojiPicker, setShowEmojiPicker] = useState(false);
22
+ const containerRef = useRef(null);
23
+ const { isOpenCanned, setIsOpenCanned } = useMessageFooterContext();
24
+ const isCrm = useAuthStore((state) => state.isCrm);
25
+ const handleEmojiSelect = useCallback((emoji) => {
26
+ editor.update(() => {
27
+ const selection = $getSelection();
28
+ if ($isRangeSelection(selection)) {
29
+ selection.insertText(emoji);
30
+ }
31
+ });
32
+ setShowEmojiPicker(false);
33
+ }, [editor]);
34
+ const beforeUploadImagesAndVideo = (file, fileList) => {
35
+ const validation = validateFile(file, t);
36
+ if (!validation.isValid) {
37
+ antdMessage.error(validation.error);
38
+ return Upload.LIST_IGNORE;
39
+ }
40
+ const videoValidation = validateVideoLimit([file], listUploadFiles, t);
41
+ if (!videoValidation.isValid) {
42
+ antdMessage.error(videoValidation.error);
43
+ return Upload.LIST_IGNORE;
44
+ }
45
+ const newVideos = fileList.filter((f) => { var _a; return (_a = f.type) === null || _a === void 0 ? void 0 : _a.startsWith("video/"); });
46
+ if (newVideos.length > 1) {
47
+ antdMessage.error(t("video_limit_exceeded"));
48
+ return Upload.LIST_IGNORE;
49
+ }
50
+ return false;
51
+ };
52
+ const beforeUploadFile = (file) => {
53
+ const isAllowed = DOCUMENT_TYPES.includes(file.type);
54
+ if (!isAllowed) {
55
+ antdMessage.error(t("invalid_document_format", { fileName: file.name }));
56
+ return Upload.LIST_IGNORE;
57
+ }
58
+ return false;
59
+ };
60
+ const handleChange = (info) => {
61
+ var _a;
62
+ let newList = [...info.fileList];
63
+ const lastFile = info.file;
64
+ if (DOCUMENT_TYPES.includes(lastFile.type || "")) {
65
+ newList = newList.filter((f) => !DOCUMENT_TYPES.includes(f.type || ""));
66
+ const originFile = ((_a = lastFile.originFileObj) !== null && _a !== void 0 ? _a : lastFile);
67
+ newList.push(Object.assign(Object.assign({}, lastFile), { originFileObj: originFile }));
68
+ }
69
+ onFilesChange(newList);
70
+ };
71
+ const onPressCanned = useCallback(async () => {
72
+ if (!isOpenCanned) {
73
+ editor.focus();
74
+ await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for focus to complete
75
+ editor.update(() => {
76
+ const selection = $getSelection();
77
+ if ($isRangeSelection(selection)) {
78
+ selection.insertText(" #");
79
+ }
80
+ });
81
+ }
82
+ else {
83
+ setIsOpenCanned(false);
84
+ }
85
+ }, [isOpenCanned, editor]);
86
+ useEffect(() => {
87
+ const handleClickOutside = (event) => {
88
+ if (containerRef.current &&
89
+ !containerRef.current.contains(event.target)) {
90
+ setShowEmojiPicker(false);
91
+ }
92
+ };
93
+ document.addEventListener("mousedown", handleClickOutside);
94
+ return () => document.removeEventListener("mousedown", handleClickOutside);
95
+ }, []);
96
+ const btnClass = buttonClassName !== null && buttonClassName !== void 0 ? buttonClassName : "w-8 h-8 p-0 text-gray-500 hover:text-gray-700 hover:bg-gray-100";
97
+ return (_jsxs("div", { className: "flex items-center gap-1 relative", ref: containerRef, children: [isCrm && (_jsx(Button, { type: "text", shape: "default", className: clsx(btnClass, isOpenCanned && "bg-blue-100 text-blue-600"), onClick: onPressCanned, children: _jsx(CannedResponseIcon, { className: clsx("hover:text-gray-700", isOpenCanned && "text-blue-600") }) })), _jsx(Button, { type: "text", shape: "default", className: clsx(btnClass, showEmojiPicker && "bg-blue-100 text-blue-600"), onClick: () => setShowEmojiPicker(!showEmojiPicker), children: EmojiIcon }), showMediaUpload && (_jsxs(_Fragment, { children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: _jsx(Icon, { icon: "image-02-o", size: 22 }) }) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: AttachIcon }) })] })), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }));
98
+ }
@@ -0,0 +1,2 @@
1
+ export declare const QuotedMessageFooter: () => import("react/jsx-runtime").JSX.Element | null;
2
+ //# sourceMappingURL=QuotedMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuotedMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/QuotedMessage.tsx"],"names":[],"mappings":"AASA,eAAO,MAAM,mBAAmB,sDA4C/B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import useConversationStore from "../../../store/conversation";
4
+ import { Icon } from "../../icon";
5
+ import { useCallback } from "react";
6
+ import { Button } from "antd";
7
+ import { useChatContext } from "../../../context/ChatContext";
8
+ import { generateContentBasedOnMessageType } from "../../../utils/common";
9
+ export const QuotedMessageFooter = () => {
10
+ var _a;
11
+ const { t } = useTranslation();
12
+ const quotedMessage = useConversationStore((state) => state.quotedMessage);
13
+ const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
14
+ const user = useChatContext().user;
15
+ const isMe = (quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.sendID) === (user === null || user === void 0 ? void 0 : user.userID);
16
+ const handleClearQuotedMessage = useCallback(() => {
17
+ setQuotedMessage(null);
18
+ }, [setQuotedMessage]);
19
+ if (!quotedMessage)
20
+ return null;
21
+ return (_jsxs("div", { className: "flex flex-row items-center py-2 px-3 bg-blue-50 flex-1 gap-3", children: [_jsx("div", { className: "flex pr-3 items-center justify-items-center border-r-2 border-blue-500 h-full", children: _jsx(Icon, { icon: "arrow-forward-b", className: "text-blue-500", size: 16 }) }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium", children: t("reply_to", {
22
+ name: isMe ? t("your_self") : (quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.senderNickname) || "",
23
+ }) }), _jsx("p", { className: "text-sm text-gray-500 truncate", children: generateContentBasedOnMessageType(quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.contentType, (_a = quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.textElem) === null || _a === void 0 ? void 0 : _a.content) })] }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: handleClearQuotedMessage, children: _jsx(Icon, { icon: "close-o", size: 16 }) })] }));
24
+ };
@@ -0,0 +1,24 @@
1
+ import { LinkNode } from "@lexical/link";
2
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
3
+ import { ListItemNode, ListNode } from "@lexical/list";
4
+ export declare const editorTheme: {
5
+ text: {
6
+ bold: string;
7
+ italic: string;
8
+ strikethrough: string;
9
+ underline: string;
10
+ };
11
+ quote: string;
12
+ list: {
13
+ nested: {
14
+ listitem: string;
15
+ };
16
+ ol: string;
17
+ ul: string;
18
+ };
19
+ link: string;
20
+ };
21
+ export declare const editorNodes: (typeof HeadingNode | typeof ListNode | typeof ListItemNode | typeof QuoteNode | typeof LinkNode)[];
22
+ export declare const editorOnError: (error: Error) => void;
23
+ export declare const DOCUMENT_TYPES: string[];
24
+ //# sourceMappingURL=editorConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorConfig.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/editorConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEvD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;CAcvB,CAAC;AAEF,eAAO,MAAM,WAAW,qGAMvB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,SAEzC,CAAC;AAEF,eAAO,MAAM,cAAc,UAI1B,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { LinkNode } from "@lexical/link";
2
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
3
+ import { ListItemNode, ListNode } from "@lexical/list";
4
+ export const editorTheme = {
5
+ text: {
6
+ bold: "font-bold",
7
+ italic: "italic",
8
+ strikethrough: "line-through",
9
+ underline: "underline",
10
+ },
11
+ quote: "border-l-4 border-gray-300 pl-4 italic text-gray-600",
12
+ list: {
13
+ nested: { listitem: "list-none" },
14
+ ol: "list-decimal list-inside",
15
+ ul: "list-disc list-inside",
16
+ },
17
+ link: "text-blue-500 underline hover:text-blue-700",
18
+ };
19
+ export const editorNodes = [
20
+ HeadingNode,
21
+ ListNode,
22
+ ListItemNode,
23
+ QuoteNode,
24
+ LinkNode,
25
+ ];
26
+ export const editorOnError = (error) => {
27
+ console.error(error);
28
+ };
29
+ export const DOCUMENT_TYPES = [
30
+ "application/pdf",
31
+ "application/msword",
32
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
33
+ ];
@@ -2,9 +2,10 @@ import { MessageFooterContextType } from "../../../types/chat";
2
2
  import { ISessionByStatus } from "../../../store/type";
3
3
  interface MessageFooterProps {
4
4
  currentSession?: ISessionByStatus;
5
+ openCreateCannedModal?: () => void;
5
6
  }
6
7
  export declare const MessageFooterContext: import("react").Context<MessageFooterContextType>;
7
8
  export declare const useMessageFooterContext: () => MessageFooterContextType;
8
- declare const MessageFooterProvider: ({ currentSession }: MessageFooterProps) => import("react/jsx-runtime").JSX.Element;
9
+ declare const MessageFooterProvider: ({ currentSession, openCreateCannedModal, }: MessageFooterProps) => import("react/jsx-runtime").JSX.Element;
9
10
  export default MessageFooterProvider;
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AA+BD,eAAO,MAAM,oBAAoB,mDAI/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,oBAAoB,kBAAkB,4CA6DpE,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQvD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAM/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CAiHpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}