@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.
- package/dist/assets/svg/cannedResponse.d.ts +7 -0
- package/dist/assets/svg/cannedResponse.d.ts.map +1 -0
- package/dist/assets/svg/cannedResponse.js +3 -0
- package/dist/assets/svg/cannedResponse.tsx +48 -0
- package/dist/assets/svg/index.d.ts +1 -0
- package/dist/assets/svg/index.d.ts.map +1 -1
- package/dist/assets/svg/index.js +1 -0
- package/dist/assets/svg/index.ts +1 -0
- package/dist/components/cannedResponse/CannedResponseBody.d.ts +8 -0
- package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseBody.js +58 -0
- package/dist/components/cannedResponse/CannedResponseFooter.d.ts +6 -0
- package/dist/components/cannedResponse/CannedResponseFooter.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseFooter.js +8 -0
- package/dist/components/cannedResponse/CannedResponseHeader.d.ts +8 -0
- package/dist/components/cannedResponse/CannedResponseHeader.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseHeader.js +11 -0
- package/dist/components/cannedResponse/index.d.ts +8 -0
- package/dist/components/cannedResponse/index.d.ts.map +1 -0
- package/dist/components/cannedResponse/index.js +34 -0
- package/dist/components/cannedResponse/team/TeamItem.d.ts +11 -0
- package/dist/components/cannedResponse/team/TeamItem.d.ts.map +1 -0
- package/dist/components/cannedResponse/team/TeamItem.js +31 -0
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +6 -2
- package/dist/components/mediaCollection/LinkCollection.js +1 -1
- package/dist/components/message/MediaPreviewIcon.d.ts +7 -0
- package/dist/components/message/MediaPreviewIcon.d.ts.map +1 -0
- package/dist/components/message/MediaPreviewIcon.js +24 -0
- package/dist/components/message/MessageHeader.js +1 -1
- package/dist/components/message/MessageList.d.ts +1 -0
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +49 -6
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
- package/dist/components/message/footer/ActionBar.js +15 -86
- package/dist/components/message/footer/CannedResponsePlugin.d.ts +7 -0
- package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -0
- package/dist/components/message/footer/CannedResponsePlugin.js +31 -0
- package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
- package/dist/components/message/footer/EmojiPicker.js +9 -5
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
- package/dist/components/message/footer/EnterHandler.js +16 -5
- package/dist/components/message/footer/FilePreview.d.ts +5 -0
- package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
- package/dist/components/message/footer/FilePreview.js +15 -12
- package/dist/components/message/footer/MediaActions.d.ts +10 -0
- package/dist/components/message/footer/MediaActions.d.ts.map +1 -0
- package/dist/components/message/footer/MediaActions.js +98 -0
- package/dist/components/message/footer/QuotedMessage.d.ts +2 -0
- package/dist/components/message/footer/QuotedMessage.d.ts.map +1 -0
- package/dist/components/message/footer/QuotedMessage.js +24 -0
- package/dist/components/message/footer/editorConfig.d.ts +24 -0
- package/dist/components/message/footer/editorConfig.d.ts.map +1 -0
- package/dist/components/message/footer/editorConfig.js +33 -0
- package/dist/components/message/footer/index.d.ts +2 -1
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +33 -28
- package/dist/components/message/item/QuoteMessage.d.ts +9 -0
- package/dist/components/message/item/QuoteMessage.d.ts.map +1 -0
- package/dist/components/message/item/QuoteMessage.js +41 -0
- package/dist/components/message/item/RevokeMessage.d.ts +5 -0
- package/dist/components/message/item/RevokeMessage.d.ts.map +1 -0
- package/dist/components/message/item/RevokeMessage.js +8 -0
- package/dist/components/message/item/TextMessage.js +1 -1
- package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
- package/dist/components/message/item/UrlTextMessage.js +3 -3
- package/dist/components/message/item/index.d.ts +6 -1
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +79 -25
- package/dist/components/richTextEditor/RichTextEditor.d.ts +12 -0
- package/dist/components/richTextEditor/RichTextEditor.d.ts.map +1 -0
- package/dist/components/richTextEditor/RichTextEditor.js +62 -0
- package/dist/components/searchConversation/SearchDrawer.js +1 -1
- package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts +3 -1
- package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts.map +1 -1
- package/dist/components/searchConversation/item/SearchItemAsMessage.js +5 -2
- package/dist/components/thread/AssignConfirmModal.d.ts +12 -0
- package/dist/components/thread/AssignConfirmModal.d.ts.map +1 -0
- package/dist/components/thread/AssignConfirmModal.js +11 -0
- package/dist/components/thread/ManualAssignPopover.d.ts +14 -0
- package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -0
- package/dist/components/thread/ManualAssignPopover.js +83 -0
- package/dist/components/thread/SessionSection.d.ts.map +1 -1
- package/dist/components/thread/SessionSection.js +11 -6
- package/dist/components/thread/UserSection.js +1 -1
- package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts +3 -0
- package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts.map +1 -0
- package/dist/hooks/cannedResponse/useFetchCannedCategories.js +13 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts +219 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts.map +1 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.js +55 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +18 -1
- package/dist/hooks/message/useRevokeMessage.d.ts +5 -0
- package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -0
- package/dist/hooks/message/useRevokeMessage.js +16 -0
- package/dist/hooks/message/useSendMessage.d.ts +1 -0
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/message/useSendMessage.js +40 -9
- package/dist/hooks/session/useAssignSession.d.ts +8 -0
- package/dist/hooks/session/useAssignSession.d.ts.map +1 -0
- package/dist/hooks/session/useAssignSession.js +15 -0
- package/dist/hooks/session/useCreateNote.d.ts.map +1 -1
- package/dist/hooks/session/useCreateNote.js +2 -1
- package/dist/hooks/session/useGetTeamSupporters.d.ts +8 -0
- package/dist/hooks/session/useGetTeamSupporters.d.ts.map +1 -0
- package/dist/hooks/session/useGetTeamSupporters.js +20 -0
- package/dist/hooks/team/useFetchMyTeam.d.ts +3 -0
- package/dist/hooks/team/useFetchMyTeam.d.ts.map +1 -0
- package/dist/hooks/team/useFetchMyTeam.js +12 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/locales/vi/common.json +54 -53
- package/dist/screens/deskMessage/index.d.ts +4 -1
- package/dist/screens/deskMessage/index.d.ts.map +1 -1
- package/dist/screens/deskMessage/index.js +2 -2
- package/dist/services/query.d.ts +5 -0
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +5 -0
- package/dist/services/routes.d.ts +5 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +5 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +7 -1
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/chat.d.ts +7 -1
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/chat.js +5 -0
- package/dist/types/dto.d.ts +53 -1
- package/dist/types/dto.d.ts.map +1 -1
- package/dist/utils/common.d.ts +3 -2
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +43 -19
- package/dist/utils/fileValidation.d.ts.map +1 -1
- package/dist/utils/fileValidation.js +2 -8
- package/dist/utils/queryHelpers.d.ts +3 -0
- package/dist/utils/queryHelpers.d.ts.map +1 -0
- package/dist/utils/queryHelpers.js +11 -0
- 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,
|
|
4
|
-
import { Button
|
|
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
|
|
15
|
-
import
|
|
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
|
|
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
|
|
31
|
-
|
|
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 (
|
|
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",
|
|
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 @@
|
|
|
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":"
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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-
|
|
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":"
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
}, [
|
|
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":"
|
|
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
|
-
|
|
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
|
-
|
|
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: () =>
|
|
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: () =>
|
|
46
|
-
}, [
|
|
47
|
-
|
|
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 @@
|
|
|
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":"
|
|
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"}
|