@droppii-org/chat-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  2. package/dist/components/conversation/ConversationBySessionItem.js +6 -2
  3. package/dist/components/mediaCollection/LinkCollection.js +1 -1
  4. package/dist/components/message/MediaPreviewIcon.d.ts +7 -0
  5. package/dist/components/message/MediaPreviewIcon.d.ts.map +1 -0
  6. package/dist/components/message/MediaPreviewIcon.js +24 -0
  7. package/dist/components/message/MessageHeader.js +1 -1
  8. package/dist/components/message/MessageList.d.ts.map +1 -1
  9. package/dist/components/message/MessageList.js +48 -5
  10. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  11. package/dist/components/message/footer/ActionBar.js +15 -86
  12. package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
  13. package/dist/components/message/footer/EmojiPicker.js +9 -5
  14. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  15. package/dist/components/message/footer/EnterHandler.js +16 -5
  16. package/dist/components/message/footer/FilePreview.d.ts +5 -0
  17. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  18. package/dist/components/message/footer/FilePreview.js +15 -12
  19. package/dist/components/message/footer/MediaActions.d.ts +10 -0
  20. package/dist/components/message/footer/MediaActions.d.ts.map +1 -0
  21. package/dist/components/message/footer/MediaActions.js +78 -0
  22. package/dist/components/message/footer/QuotedMessage.d.ts +2 -0
  23. package/dist/components/message/footer/QuotedMessage.d.ts.map +1 -0
  24. package/dist/components/message/footer/QuotedMessage.js +24 -0
  25. package/dist/components/message/footer/editorConfig.d.ts +24 -0
  26. package/dist/components/message/footer/editorConfig.d.ts.map +1 -0
  27. package/dist/components/message/footer/editorConfig.js +33 -0
  28. package/dist/components/message/footer/index.d.ts.map +1 -1
  29. package/dist/components/message/footer/index.js +6 -27
  30. package/dist/components/message/item/QuoteMessage.d.ts +9 -0
  31. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -0
  32. package/dist/components/message/item/QuoteMessage.js +22 -0
  33. package/dist/components/message/item/RevokeMessage.d.ts +5 -0
  34. package/dist/components/message/item/RevokeMessage.d.ts.map +1 -0
  35. package/dist/components/message/item/RevokeMessage.js +8 -0
  36. package/dist/components/message/item/TextMessage.js +1 -1
  37. package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
  38. package/dist/components/message/item/UrlTextMessage.js +3 -3
  39. package/dist/components/message/item/index.d.ts +6 -1
  40. package/dist/components/message/item/index.d.ts.map +1 -1
  41. package/dist/components/message/item/index.js +79 -25
  42. package/dist/components/richTextEditor/RichTextEditor.d.ts +12 -0
  43. package/dist/components/richTextEditor/RichTextEditor.d.ts.map +1 -0
  44. package/dist/components/richTextEditor/RichTextEditor.js +62 -0
  45. package/dist/components/searchConversation/SearchDrawer.js +1 -1
  46. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts +3 -1
  47. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts.map +1 -1
  48. package/dist/components/searchConversation/item/SearchItemAsMessage.js +5 -2
  49. package/dist/components/thread/AssignConfirmModal.d.ts +12 -0
  50. package/dist/components/thread/AssignConfirmModal.d.ts.map +1 -0
  51. package/dist/components/thread/AssignConfirmModal.js +11 -0
  52. package/dist/components/thread/ManualAssignPopover.d.ts +14 -0
  53. package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -0
  54. package/dist/components/thread/ManualAssignPopover.js +83 -0
  55. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  56. package/dist/components/thread/SessionSection.js +11 -6
  57. package/dist/components/thread/UserSection.js +1 -1
  58. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  59. package/dist/hooks/message/useMessage.js +1 -0
  60. package/dist/hooks/message/useRevokeMessage.d.ts +5 -0
  61. package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -0
  62. package/dist/hooks/message/useRevokeMessage.js +16 -0
  63. package/dist/hooks/message/useSendMessage.d.ts +1 -0
  64. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  65. package/dist/hooks/message/useSendMessage.js +40 -9
  66. package/dist/hooks/session/useAssignSession.d.ts +8 -0
  67. package/dist/hooks/session/useAssignSession.d.ts.map +1 -0
  68. package/dist/hooks/session/useAssignSession.js +15 -0
  69. package/dist/hooks/session/useCreateNote.d.ts.map +1 -1
  70. package/dist/hooks/session/useCreateNote.js +2 -1
  71. package/dist/hooks/session/useGetTeamSupporters.d.ts +8 -0
  72. package/dist/hooks/session/useGetTeamSupporters.d.ts.map +1 -0
  73. package/dist/hooks/session/useGetTeamSupporters.js +20 -0
  74. package/dist/index.d.ts +1 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +1 -0
  77. package/dist/locales/vi/common.json +48 -53
  78. package/dist/services/query.d.ts +2 -0
  79. package/dist/services/query.d.ts.map +1 -1
  80. package/dist/services/query.js +2 -0
  81. package/dist/services/routes.d.ts +2 -0
  82. package/dist/services/routes.d.ts.map +1 -1
  83. package/dist/services/routes.js +2 -0
  84. package/dist/store/conversation.d.ts.map +1 -1
  85. package/dist/store/conversation.js +7 -1
  86. package/dist/styles/global.css +1 -1
  87. package/dist/tsconfig.tsbuildinfo +1 -0
  88. package/dist/types/chat.d.ts +1 -1
  89. package/dist/types/chat.d.ts.map +1 -1
  90. package/dist/types/dto.d.ts +26 -0
  91. package/dist/types/dto.d.ts.map +1 -1
  92. package/dist/utils/common.d.ts +3 -2
  93. package/dist/utils/common.d.ts.map +1 -1
  94. package/dist/utils/common.js +43 -19
  95. package/dist/utils/fileValidation.d.ts.map +1 -1
  96. package/dist/utils/fileValidation.js +2 -8
  97. package/dist/utils/queryHelpers.d.ts +3 -0
  98. package/dist/utils/queryHelpers.d.ts.map +1 -0
  99. package/dist/utils/queryHelpers.js +11 -0
  100. package/package.json +1 -1
@@ -0,0 +1,78 @@
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
+ 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" }) }));
14
+ 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" }) }));
15
+ export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload = true, }) {
16
+ const [editor] = useLexicalComposerContext();
17
+ const { t } = useTranslation();
18
+ const [showEmojiPicker, setShowEmojiPicker] = useState(false);
19
+ const containerRef = useRef(null);
20
+ const handleEmojiSelect = useCallback((emoji) => {
21
+ editor.update(() => {
22
+ const selection = $getSelection();
23
+ if ($isRangeSelection(selection)) {
24
+ selection.insertText(emoji);
25
+ }
26
+ });
27
+ setShowEmojiPicker(false);
28
+ }, [editor]);
29
+ const beforeUploadImagesAndVideo = (file, fileList) => {
30
+ const validation = validateFile(file, t);
31
+ if (!validation.isValid) {
32
+ antdMessage.error(validation.error);
33
+ return Upload.LIST_IGNORE;
34
+ }
35
+ const videoValidation = validateVideoLimit([file], listUploadFiles, t);
36
+ if (!videoValidation.isValid) {
37
+ antdMessage.error(videoValidation.error);
38
+ return Upload.LIST_IGNORE;
39
+ }
40
+ const newVideos = fileList.filter((f) => { var _a; return (_a = f.type) === null || _a === void 0 ? void 0 : _a.startsWith("video/"); });
41
+ if (newVideos.length > 1) {
42
+ antdMessage.error(t("video_limit_exceeded"));
43
+ return Upload.LIST_IGNORE;
44
+ }
45
+ return false;
46
+ };
47
+ const beforeUploadFile = (file) => {
48
+ const isAllowed = DOCUMENT_TYPES.includes(file.type);
49
+ if (!isAllowed) {
50
+ antdMessage.error(t("invalid_document_format", { fileName: file.name }));
51
+ return Upload.LIST_IGNORE;
52
+ }
53
+ return false;
54
+ };
55
+ const handleChange = (info) => {
56
+ var _a;
57
+ let newList = [...info.fileList];
58
+ const lastFile = info.file;
59
+ if (DOCUMENT_TYPES.includes(lastFile.type || "")) {
60
+ newList = newList.filter((f) => !DOCUMENT_TYPES.includes(f.type || ""));
61
+ const originFile = ((_a = lastFile.originFileObj) !== null && _a !== void 0 ? _a : lastFile);
62
+ newList.push(Object.assign(Object.assign({}, lastFile), { originFileObj: originFile }));
63
+ }
64
+ onFilesChange(newList);
65
+ };
66
+ useEffect(() => {
67
+ const handleClickOutside = (event) => {
68
+ if (containerRef.current &&
69
+ !containerRef.current.contains(event.target)) {
70
+ setShowEmojiPicker(false);
71
+ }
72
+ };
73
+ document.addEventListener("mousedown", handleClickOutside);
74
+ return () => document.removeEventListener("mousedown", handleClickOutside);
75
+ }, []);
76
+ 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";
77
+ return (_jsxs("div", { className: "flex items-center gap-1 relative", ref: containerRef, children: [_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) }))] }));
78
+ }
@@ -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
+ ];
@@ -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;AAIvD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AASD,eAAO,MAAM,oBAAoB,mDAI/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,oBAAoB,kBAAkB,4CA8DpE,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -7,9 +7,7 @@ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
7
7
  import { ToolbarPlugin } from "./ToolbarPlugin";
8
8
  import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
9
9
  import { ListPlugin } from "@lexical/react/LexicalListPlugin";
10
- import { LinkNode } from "@lexical/link";
11
- import { HeadingNode, QuoteNode } from "@lexical/rich-text";
12
- import { ListItemNode, ListNode } from "@lexical/list";
10
+ import { editorTheme, editorNodes, editorOnError } from "./editorConfig";
13
11
  import { createContext, useCallback, useContext, useState } from "react";
14
12
  import EnterHandler from "./EnterHandler";
15
13
  import ActionBar from "./ActionBar";
@@ -17,31 +15,12 @@ import { useSendMessage } from "../../../hooks/message/useSendMessage";
17
15
  import FilePreview from "./FilePreview";
18
16
  import { useTranslation } from "react-i18next";
19
17
  import PasteAndDropPlugin from "./PasteAndDropPlugin";
20
- const theme = {
21
- text: {
22
- bold: "font-bold",
23
- italic: "italic",
24
- strikethrough: "line-through",
25
- underline: "underline",
26
- },
27
- quote: "border-l-4 border-gray-300 pl-4 italic text-gray-600",
28
- list: {
29
- nested: {
30
- listitem: "list-none",
31
- },
32
- ol: "list-decimal list-inside",
33
- ul: "list-disc list-inside",
34
- },
35
- link: "text-blue-500 underline hover:text-blue-700",
36
- };
37
- const onError = (error) => {
38
- console.error(error);
39
- };
18
+ import { QuotedMessageFooter } from "./QuotedMessage";
40
19
  const initialConfig = {
41
20
  namespace: "ChatInput",
42
- theme,
43
- onError,
44
- nodes: [HeadingNode, ListNode, ListItemNode, QuoteNode, LinkNode],
21
+ theme: editorTheme,
22
+ onError: editorOnError,
23
+ nodes: editorNodes,
45
24
  };
46
25
  export const MessageFooterContext = createContext({
47
26
  onSendMessage: () => { },
@@ -67,6 +46,6 @@ const MessageFooterProvider = ({ currentSession }) => {
67
46
  }
68
47
  setListUploadFiles([]);
69
48
  }, [sendMergeMessage, sendTextMessage, listUploadFiles, currentSession]);
70
- return (_jsx(MessageFooterContext.Provider, { value: { onSendMessage, listUploadFiles, setListUploadFiles }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [listUploadFiles.length > 0 && _jsx(FilePreview, {}), _jsx(ToolbarPlugin, {}), _jsx("div", { className: "relative px-4", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "border border-indigo-500 rounded-md bg-blue-100 min-h-[64px] max-h-[140px] overflow-y-auto px-3 py-2 text-sm" }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": t("enter_message"), placeholder: _jsx("div", { className: "absolute top-2 left-7 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: t("enter_message") }) }) }) }), _jsx(ActionBar, {})] }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {})] }) }));
49
+ return (_jsx(MessageFooterContext.Provider, { value: { onSendMessage, listUploadFiles, setListUploadFiles }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(QuotedMessageFooter, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), _jsx(ToolbarPlugin, {}), _jsx("div", { className: "relative px-4", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "border border-indigo-500 rounded-md bg-blue-100 min-h-[64px] max-h-[140px] overflow-y-auto px-3 py-2 text-sm" }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": t("enter_message"), placeholder: _jsx("div", { className: "absolute top-2 left-7 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: t("enter_message") }) }) }) }), _jsx(ActionBar, {})] }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {})] }) }));
71
50
  };
72
51
  export default MessageFooterProvider;
@@ -0,0 +1,9 @@
1
+ import { MessageItem } from "@openim/wasm-client-sdk";
2
+ interface QuoteMessageItemProps {
3
+ message: MessageItem;
4
+ isMine: boolean;
5
+ onPressQuoteMessage?: (clientMsgID: string) => void;
6
+ }
7
+ declare const QuoteMessageItem: ({ message, isMine, onPressQuoteMessage, }: QuoteMessageItemProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default QuoteMessageItem;
9
+ //# sourceMappingURL=QuoteMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuoteMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/QuoteMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAOnE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,gBAAgB,GAAI,2CAIvB,qBAAqB,4CAwCvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { generateContentBasedOnMessageType } from "../../../utils/common";
3
+ import { Icon } from "../../icon";
4
+ import { useTranslation } from "react-i18next";
5
+ import clsx from "clsx";
6
+ import MediaPreviewIcon from "../MediaPreviewIcon";
7
+ const QuoteMessageItem = ({ message, isMine, onPressQuoteMessage, }) => {
8
+ var _a, _b, _c, _d, _e, _f, _g, _h;
9
+ const { t } = useTranslation();
10
+ const from = isMine ? t("you") : message === null || message === void 0 ? void 0 : message.senderNickname;
11
+ const to = isMine
12
+ ? t("your_self")
13
+ : (_b = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage) === null || _b === void 0 ? void 0 : _b.senderNickname;
14
+ return (_jsxs("div", { className: clsx("mb-[-4px] flex flex-col gap-1", isMine ? "items-end" : "items-start"), children: [_jsxs("div", { className: "flex flex-row items-center gap-2", children: [_jsx(Icon, { icon: "arrow-reply-b", size: 16, className: "text-blue-500" }), _jsx("span", { className: "text-xs text-gray-500", children: t("replied_to", {
15
+ from: from,
16
+ to: to,
17
+ }) })] }), _jsxs("div", { className: "flex flex-row items-center gap-2 border border-gray-200 rounded-2xl min-w-0 px-3 py-2 bg-gray-100 cursor-pointer", onClick: () => {
18
+ var _a, _b;
19
+ return onPressQuoteMessage === null || onPressQuoteMessage === void 0 ? void 0 : onPressQuoteMessage(((_b = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage) === null || _b === void 0 ? void 0 : _b.clientMsgID) || "");
20
+ }, children: [_jsx(MediaPreviewIcon, { message: (_c = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _c === void 0 ? void 0 : _c.quoteMessage }), _jsx("span", { className: "text-sm text-gray-500 truncate", children: generateContentBasedOnMessageType((_e = (_d = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _d === void 0 ? void 0 : _d.quoteMessage) === null || _e === void 0 ? void 0 : _e.contentType, ((_h = (_g = (_f = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _f === void 0 ? void 0 : _f.quoteMessage) === null || _g === void 0 ? void 0 : _g.textElem) === null || _h === void 0 ? void 0 : _h.content) || "") })] })] }));
21
+ };
22
+ export default QuoteMessageItem;
@@ -0,0 +1,5 @@
1
+ interface RevokeMessageItemProps {
2
+ }
3
+ declare const RevokeMessageItem: (_: RevokeMessageItemProps) => import("react/jsx-runtime").JSX.Element;
4
+ export default RevokeMessageItem;
5
+ //# sourceMappingURL=RevokeMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RevokeMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/RevokeMessage.tsx"],"names":[],"mappings":"AAIA,UAAU,sBAAsB;CAAG;AACnC,QAAA,MAAM,iBAAiB,GAAI,GAAG,sBAAsB,4CAQnD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useTranslation } from "react-i18next";
4
+ const RevokeMessageItem = (_) => {
5
+ const { t } = useTranslation();
6
+ return (_jsx("span", { className: "!text-sm sm:text-base whitespace-pre-wrap italic text-gray-500", children: t("revoked") }));
7
+ };
8
+ export default RevokeMessageItem;
@@ -23,7 +23,7 @@ const TextMessageItem = (props) => {
23
23
  const htmlContent = ((_b = (_a = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.content) || "";
24
24
  // 🔒 Sanitize HTML to prevent XSS attacks
25
25
  const sanitizedContent = sanitizeHtml(htmlContent);
26
- return (_jsx("div", { className: "!text-sm sm:text-base break-words whitespace-pre-line flex-1", style: { wordBreak: "break-word", overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }));
26
+ return (_jsx("div", { className: "!text-sm sm:text-base break-normal whitespace-pre-line flex-1", style: { overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }));
27
27
  }
28
28
  return (_jsx("span", { className: "!text-sm sm:text-base whitespace-pre-wrap", children: ((_c = message === null || message === void 0 ? void 0 : message.textElem) === null || _c === void 0 ? void 0 : _c.content) || "" }));
29
29
  };
@@ -1 +1 @@
1
- {"version":3,"file":"UrlTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/UrlTextMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMtD,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,QAAA,MAAM,kBAAkB,GAAI,OAAO,uBAAuB,4CAwEzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"UrlTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/UrlTextMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAUtD,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,QAAA,MAAM,kBAAkB,GAAI,OAAO,uBAAuB,4CAwEzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { getHostFromUrl, wrapLinksInHtml, sanitizeHtml } from "../../../utils/common";
3
+ import { getHostFromUrl, wrapLinksInHtml, sanitizeHtml, } from "../../../utils/common";
4
4
  import { useFetchExternalLink } from "../../../hooks/common/useFetchExternalLink";
5
5
  import { Image } from "antd";
6
6
  const UrlTextMessageItem = (props) => {
@@ -25,8 +25,8 @@ const UrlTextMessageItem = (props) => {
25
25
  const htmlContent = ((_d = (_c = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.content) || "";
26
26
  // 🔒 Sanitize HTML to prevent XSS attacks
27
27
  const sanitizedContent = sanitizeHtml(wrapLinksInHtml(htmlContent));
28
- return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "!text-sm sm:text-base break-words whitespace-pre-line flex-1", style: { wordBreak: "break-word", overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }), !!externalLinkData && (_jsxs("div", { className: "flex items-center rounded-md px-3 py-2 border-l-4 border-l-blue-500 bg-blue-50 gap-2 cursor-pointer overflow-hidden", style: { wordBreak: "break-word", overflowWrap: "anywhere" }, onClick: () => onPressExternalLink((externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) || ""), children: [_jsxs("div", { className: "flex flex-1 flex-col gap-1", children: [(externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.title) && (_jsx("span", { className: "!text-sm font-semibold truncate", children: externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.title })), (externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.description) && (_jsx("span", { className: "!text-sm text-gray-500 line-clamp-2", children: externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.description })), (externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) && (_jsx("span", { className: "!text-sm font-medium text-blue-500", children: getHostFromUrl(externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) }))] }), ((_e = externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.logo) === null || _e === void 0 ? void 0 : _e.url) && (_jsx(Image, { src: (_f = externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.logo) === null || _f === void 0 ? void 0 : _f.url, width: 48, height: 48 }))] }))] }));
28
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "!text-sm sm:text-base break-normal whitespace-pre-line flex-1", style: { overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }), !!externalLinkData && (_jsxs("div", { className: "flex items-center rounded-md px-3 py-2 border-l-4 border-l-blue-500 bg-blue-50 gap-2 cursor-pointer overflow-hidden", style: { wordBreak: "break-word", overflowWrap: "anywhere" }, onClick: () => onPressExternalLink((externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) || ""), children: [_jsxs("div", { className: "flex flex-1 flex-col gap-1", children: [(externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.title) && (_jsx("span", { className: "!text-sm font-semibold truncate break-normal", children: externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.title })), (externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.description) && (_jsx("span", { className: "!text-sm text-gray-500 line-clamp-2", children: externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.description })), (externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) && (_jsx("span", { className: "!text-sm font-medium text-blue-500", children: getHostFromUrl(externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.url) }))] }), ((_e = externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.logo) === null || _e === void 0 ? void 0 : _e.url) && (_jsx(Image, { src: (_f = externalLinkData === null || externalLinkData === void 0 ? void 0 : externalLinkData.logo) === null || _f === void 0 ? void 0 : _f.url, width: 48, height: 48 }))] }))] }));
29
29
  }
30
- return (_jsx("span", { className: "!text-sm sm:text-base break-words whitespace-pre-line flex-1", style: { wordBreak: "break-word", overflowWrap: "anywhere" }, children: ((_h = (_g = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _g === void 0 ? void 0 : _g.urls) === null || _h === void 0 ? void 0 : _h[0]) || "" }));
30
+ return (_jsx("span", { className: "!text-sm sm:text-base break-normal whitespace-pre-line flex-1", style: { overflowWrap: "anywhere" }, children: ((_h = (_g = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _g === void 0 ? void 0 : _g.urls) === null || _h === void 0 ? void 0 : _h[0]) || "" }));
31
31
  };
32
32
  export default UrlTextMessageItem;
@@ -2,7 +2,12 @@ import { MessageItem as MessageItemType } from "@openim/wasm-client-sdk";
2
2
  interface MessageItemProps {
3
3
  message: MessageItemType;
4
4
  allMessages: MessageItemType[];
5
+ contextMenuOpen?: boolean;
6
+ onContextMenuOpenChange?: (open: boolean) => void;
7
+ onRevokeMessage?: (clientMsgID: string) => void;
8
+ onQuoteMessage?: (message: MessageItemType) => void;
9
+ onPressQuoteMessage?: (clientMsgID: string) => void;
5
10
  }
6
- declare const MessageItem: ({ message, allMessages }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
11
+ declare const MessageItem: ({ message, allMessages, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
7
12
  export default MessageItem;
8
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,yBAAyB,CAAC;AAmBjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,QAAA,MAAM,WAAW,GAAI,0BAA0B,gBAAgB,mDA6J9D,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,yBAAyB,CAAC;AA2BjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,WAAW,GAAI,2HAQlB,gBAAgB,mDA0PlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import dayjs from "dayjs";
3
3
  import clsx from "clsx";
4
- import { Avatar } from "antd";
4
+ import { Avatar, Dropdown } from "antd";
5
5
  import { MessageStatus, MessageType, } from "@openim/wasm-client-sdk";
6
6
  import TextMessageItem from "./TextMessage";
7
7
  import ImageMessageItem from "./ImageMessage";
@@ -12,32 +12,26 @@ import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../../constants";
12
12
  import { formatTimestamp } from "../../../utils/common";
13
13
  import useAuthStore from "../../../store/auth";
14
14
  import useConversationStore from "../../../store/conversation";
15
- import { useMemo } from "react";
15
+ import { useCallback, useMemo } from "react";
16
16
  import { Trans, useTranslation } from "react-i18next";
17
17
  import UrlTextMessageItem from "./UrlTextMessage";
18
18
  import MessageStatusIndicator from "./MessageStatusIndicator";
19
- import { useSendMessage, isMediaResendable } from "../../../hooks/message/useSendMessage";
20
- const MessageItem = ({ message, allMessages }) => {
19
+ import { useSendMessage, isMediaResendable, } from "../../../hooks/message/useSendMessage";
20
+ import { Icon } from "../../icon";
21
+ import RevokeMessageItem from "./RevokeMessage";
22
+ import QuoteMessageItem from "./QuoteMessage";
23
+ const ICON_COLOR = "#3278f7";
24
+ const MessageItem = ({ message, allMessages, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, }) => {
21
25
  var _a, _b, _c, _d, _e;
22
26
  const { t } = useTranslation();
23
27
  const { resendMessage } = useSendMessage();
24
28
  const userID = useAuthStore((state) => state.userID);
25
29
  const isCrm = useAuthStore((state) => state.isCrm);
26
30
  const conversationData = useConversationStore((state) => state.conversationData);
27
- const showSenderInfo = useMemo(() => {
28
- var _a, _b, _c;
29
- try {
30
- const isChatSupport = (_c = (_b = (_a = JSON.parse((conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _a === void 0 ? void 0 : _a.sessionInfo) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.botId;
31
- return isChatSupport && isCrm;
32
- }
33
- catch (_d) {
34
- console.log("Failed to parse conversationData.ex");
35
- }
36
- }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex, isCrm]);
37
- const isVisibleGroup = visibleTypeMessage.includes(message === null || message === void 0 ? void 0 : message.contentType);
38
- const renderMessageByType = (message) => {
31
+ const renderMessageByType = useCallback((message) => {
39
32
  switch (message === null || message === void 0 ? void 0 : message.contentType) {
40
33
  case MessageType.TextMessage:
34
+ case MessageType.QuoteMessage:
41
35
  return _jsx(TextMessageItem, { message: message });
42
36
  case MessageType.PictureMessage:
43
37
  return _jsx(ImageMessageItem, { message: message });
@@ -47,13 +41,68 @@ const MessageItem = ({ message, allMessages }) => {
47
41
  return _jsx(VideoMessageItem, { message: message });
48
42
  case MessageType.UrlTextMessage:
49
43
  return _jsx(UrlTextMessageItem, { message: message });
44
+ case MessageType.RevokeMessage:
45
+ return _jsx(RevokeMessageItem, {});
50
46
  default:
51
47
  return _jsx(TextMessageItem, { message: message });
52
48
  }
53
- };
54
- if (!isVisibleGroup)
55
- return null;
56
- const isMine = (message === null || message === void 0 ? void 0 : message.sendID) === userID;
49
+ }, [message]);
50
+ const { isReplyable, isRevocable, isMine } = useMemo(() => {
51
+ const isMyMessage = (message === null || message === void 0 ? void 0 : message.sendID) === userID;
52
+ const isOlderThanOneDay = dayjs().diff(dayjs(message === null || message === void 0 ? void 0 : message.sendTime), "days", true) >= 1;
53
+ return {
54
+ isReplyable: message.status === MessageStatus.Succeed,
55
+ isRevocable: isMyMessage &&
56
+ message.status === MessageStatus.Succeed &&
57
+ !isOlderThanOneDay,
58
+ isMine: isMyMessage,
59
+ };
60
+ }, [userID, message]);
61
+ const contextMenuItems = useMemo(() => {
62
+ if ((message === null || message === void 0 ? void 0 : message.contentType) === MessageType.RevokeMessage)
63
+ return [];
64
+ return [
65
+ ...(isReplyable
66
+ ? [
67
+ {
68
+ key: "TB01",
69
+ label: t("context_menu_reply"),
70
+ icon: (_jsx(Icon, { icon: "arrow-reply-alt-o", size: 18, color: ICON_COLOR })),
71
+ onClick: () => onQuoteMessage === null || onQuoteMessage === void 0 ? void 0 : onQuoteMessage(message),
72
+ },
73
+ ]
74
+ : []),
75
+ ...(isRevocable
76
+ ? [
77
+ {
78
+ key: "TB05",
79
+ label: t("context_menu_revoke"),
80
+ icon: _jsx(Icon, { icon: "arrow-reload-o", size: 18, color: ICON_COLOR }),
81
+ onClick: () => onRevokeMessage === null || onRevokeMessage === void 0 ? void 0 : onRevokeMessage((message === null || message === void 0 ? void 0 : message.clientMsgID) || ""),
82
+ },
83
+ ]
84
+ : []),
85
+ ];
86
+ }, [
87
+ isReplyable,
88
+ isRevocable,
89
+ t,
90
+ onRevokeMessage,
91
+ message === null || message === void 0 ? void 0 : message.contentType,
92
+ message === null || message === void 0 ? void 0 : message.clientMsgID,
93
+ onQuoteMessage,
94
+ ]);
95
+ const showSenderInfo = useMemo(() => {
96
+ var _a, _b, _c;
97
+ try {
98
+ const isChatSupport = (_c = (_b = (_a = JSON.parse((conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _a === void 0 ? void 0 : _a.sessionInfo) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.botId;
99
+ return isChatSupport && isCrm && !isMine;
100
+ }
101
+ catch (_d) {
102
+ console.log("Failed to parse conversationData.ex");
103
+ }
104
+ }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex, isCrm, isMine]);
105
+ const hasContextMenu = contextMenuItems.length > 0;
57
106
  const isLatestMine = ((_a = allMessages.find((m) => m.sendID === userID)) === null || _a === void 0 ? void 0 : _a.clientMsgID) ===
58
107
  message.clientMsgID;
59
108
  const previousMessage = getVisibleNeighbor(allMessages, message, "prev");
@@ -65,15 +114,20 @@ const MessageItem = ({ message, allMessages }) => {
65
114
  const showTimeBreak = prevTimeBreak;
66
115
  const showSenderAvatar = !nextSameUser;
67
116
  const showSenderName = !prevSameUser && !isMine;
68
- if (!isCrm && MessageType.CustomMessage === (message === null || message === void 0 ? void 0 : message.contentType)) {
117
+ if (!isCrm &&
118
+ (MessageType.CustomMessage === (message === null || message === void 0 ? void 0 : message.contentType) ||
119
+ !visibleTypeMessage.includes(message === null || message === void 0 ? void 0 : message.contentType))) {
69
120
  return null;
70
121
  }
71
122
  return (_jsxs("div", { className: "flex flex-col gap-2 py-1 px-3 sm:px-4 min-w-0", id: `${MSG_ITEM_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, children: [showTimeBreak && (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "text-xs text-gray-600 text-center bg-neutral-100 px-2 py-1 rounded-full", children: formatTimestamp(message.sendTime, {
72
123
  dateMonthFormat: "DD MMMM",
73
- }) }) })), _jsx("div", { className: clsx("flex min-w-0", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: clsx("flex flex-1 items-end gap-2 min-w-0", isMine ? "justify-end" : "justify-start"), children: [!isMine && showSenderInfo && (_jsx("div", { className: "flex items-center justify-center w-[32px] h-[32px]", children: showSenderAvatar && (_jsx(Avatar, { src: message === null || message === void 0 ? void 0 : message.senderFaceUrl, children: ((_c = (_b = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _b === void 0 ? void 0 : _b.charAt) === null || _c === void 0 ? void 0 : _c.call(_b, 0)) || "A" })) })), _jsxs("div", { className: clsx("flex flex-col flex-[0.8] min-w-0", isMine ? "items-end" : "items-start"), children: [showSenderName && showSenderInfo && (_jsx("span", { className: "text-xs text-gray-500 mb-1 px-3", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsxs("div", { className: "flex items-end gap-1.5", children: [isMine && _jsx(MessageStatusIndicator, { message: message, isLatest: isLatestMine }), _jsxs("div", { className: clsx("px-3 py-2 rounded-2xl max-w-full min-w-0 break-words flex flex-col flex-1 text-gray-900 gap-1", isMine ? "bg-blue-100" : "bg-white"), style: { wordBreak: "break-word", overflowWrap: "anywhere" }, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, children: [(message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage ? (_jsxs("div", { children: [(_e = (_d = message === null || message === void 0 ? void 0 : message.mergeElem) === null || _d === void 0 ? void 0 : _d.multiMessage) === null || _e === void 0 ? void 0 : _e.map((item) => {
74
- return renderMessageByType(item);
75
- }), (message === null || message === void 0 ? void 0 : message.textElem) && _jsx(TextMessageItem, { message: message })] })) : (renderMessageByType(message)), _jsx("span", { className: clsx("text-xs text-gray-500 text-right"), children: dayjs(message === null || message === void 0 ? void 0 : message.sendTime).format("HH:mm") })] })] }), isMine && message.status === MessageStatus.Failed && (_jsx("span", { className: "text-red-500 text-xs mt-1", children: isMediaResendable(message) ? (_jsx(Trans, { i18nKey: "message_send_failed", components: {
124
+ }) }) })), _jsx("div", { className: clsx("flex min-w-0", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: clsx("flex flex-1 items-end gap-2 min-w-0", isMine ? "justify-end" : "justify-start"), children: [showSenderInfo && (_jsx("div", { className: "flex items-center justify-center w-[32px] h-[32px]", children: showSenderAvatar && (_jsx(Avatar, { src: message === null || message === void 0 ? void 0 : message.senderFaceUrl, children: ((_c = (_b = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _b === void 0 ? void 0 : _b.charAt) === null || _c === void 0 ? void 0 : _c.call(_b, 0)) || "A" })) })), _jsxs("div", { className: clsx("flex flex-col flex-[0.8] min-w-0", isMine ? "items-end" : "items-start"), children: [showSenderName && showSenderInfo && (_jsx("span", { className: "text-xs font-bold mb-1 px-3 mt-2", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsxs("div", { className: clsx("flex flex-col flex-1", isMine ? "items-end" : "items-start"), children: [(message === null || message === void 0 ? void 0 : message.quoteElem) && (_jsx(QuoteMessageItem, { message: message, isMine: isMine, onPressQuoteMessage: onPressQuoteMessage })), _jsxs("div", { className: "flex items-end gap-1.5", children: [isMine && (_jsx(MessageStatusIndicator, { message: message, isLatest: isLatestMine })), _jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: hasContextMenu ? ["contextMenu"] : [], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, children: _jsxs("div", { className: clsx("px-3 py-2 rounded-2xl max-w-full min-w-0 break-normal flex flex-col flex-1 text-gray-900 gap-1 w-fit", isMine ? "bg-blue-100" : "bg-white"), style: {
125
+ wordBreak: "break-word",
126
+ overflowWrap: "anywhere",
127
+ }, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, children: [(message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage ? (_jsxs("div", { children: [(_e = (_d = message === null || message === void 0 ? void 0 : message.mergeElem) === null || _d === void 0 ? void 0 : _d.multiMessage) === null || _e === void 0 ? void 0 : _e.map((item) => {
128
+ return renderMessageByType(item);
129
+ }), (message === null || message === void 0 ? void 0 : message.textElem) && (_jsx(TextMessageItem, { message: message }))] })) : (renderMessageByType(message)), _jsx("span", { className: clsx("text-xs text-gray-500 text-right"), children: dayjs(message === null || message === void 0 ? void 0 : message.sendTime).format("HH:mm") })] }) })] })] }), isMine && message.status === MessageStatus.Failed && (_jsx("span", { className: "text-red-500 text-xs mt-1", children: isMediaResendable(message) ? (_jsx(Trans, { i18nKey: "message_send_failed", components: {
76
130
  bold: (_jsx("strong", { className: "cursor-pointer", onClick: () => resendMessage(message) })),
77
- } })) : (t("message_send_failed_no_retry")) }))] })] }) }, message === null || message === void 0 ? void 0 : message.clientMsgID)] }, message === null || message === void 0 ? void 0 : message.clientMsgID));
131
+ } })) : (t("message_send_failed_no_retry")) }))] })] }) })] }, message === null || message === void 0 ? void 0 : message.clientMsgID));
78
132
  };
79
133
  export default MessageItem;
@@ -0,0 +1,12 @@
1
+ import { UploadFile } from "antd";
2
+ interface RichTextEditorProps {
3
+ initialHtml?: string;
4
+ placeholder?: string;
5
+ onChange?: (html: string, plainText: string) => void;
6
+ onFilesChange?: (files: UploadFile[]) => void;
7
+ minHeight?: string;
8
+ showMediaUpload?: boolean;
9
+ }
10
+ export default function RichTextEditor({ initialHtml, placeholder, onChange, onFilesChange, minHeight, showMediaUpload, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=RichTextEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/components/richTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AA0ClC,UAAU,mBAAmB;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EACrC,WAAW,EACX,WAAW,EACX,QAAQ,EACR,aAAa,EACb,SAAmB,EACnB,eAAsB,GACvB,EAAE,mBAAmB,2CA6DrB"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
5
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
6
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
7
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
8
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
9
+ import { ListPlugin } from "@lexical/react/LexicalListPlugin";
10
+ import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
11
+ import { $generateHtmlFromNodes, $generateNodesFromDOM } from "@lexical/html";
12
+ import { $getRoot } from "lexical";
13
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
14
+ import { useEffect } from "react";
15
+ import { ToolbarPlugin } from "../message/footer/ToolbarPlugin";
16
+ import MediaActions from "../message/footer/MediaActions";
17
+ import { FilePreviewList } from "../message/footer/FilePreview";
18
+ import { editorTheme, editorNodes, editorOnError, } from "../message/footer/editorConfig";
19
+ const EDITOR_CONFIG = {
20
+ namespace: "CannedResponseEditor",
21
+ theme: editorTheme,
22
+ onError: editorOnError,
23
+ nodes: editorNodes,
24
+ };
25
+ function HtmlPlugin({ initialHtml }) {
26
+ const [editor] = useLexicalComposerContext();
27
+ useEffect(() => {
28
+ if (!initialHtml)
29
+ return;
30
+ editor.update(() => {
31
+ const parser = new DOMParser();
32
+ const dom = parser.parseFromString(initialHtml, "text/html");
33
+ const nodes = $generateNodesFromDOM(editor, dom);
34
+ const root = $getRoot();
35
+ root.clear();
36
+ root.append(...nodes);
37
+ }, { tag: "initial-html" });
38
+ }, [editor, initialHtml]);
39
+ return null;
40
+ }
41
+ export default function RichTextEditor({ initialHtml, placeholder, onChange, onFilesChange, minHeight = "160px", showMediaUpload = true, }) {
42
+ const [listUploadFiles, setListUploadFiles] = useState([]);
43
+ const handleChange = (editorState, editor, tags) => {
44
+ if (tags.has("initial-html"))
45
+ return;
46
+ editorState.read(() => {
47
+ const html = $generateHtmlFromNodes(editor);
48
+ const plainText = $getRoot().getTextContent().trim();
49
+ onChange === null || onChange === void 0 ? void 0 : onChange(html, plainText);
50
+ });
51
+ };
52
+ const handleRemoveFile = (file) => {
53
+ const newList = listUploadFiles.filter((f) => f.uid !== file.uid);
54
+ setListUploadFiles(newList);
55
+ onFilesChange === null || onFilesChange === void 0 ? void 0 : onFilesChange(newList);
56
+ };
57
+ const handleFilesChange = (files) => {
58
+ setListUploadFiles(files);
59
+ onFilesChange === null || onFilesChange === void 0 ? void 0 : onFilesChange(files);
60
+ };
61
+ return (_jsxs(LexicalComposer, { initialConfig: EDITOR_CONFIG, children: [_jsxs("div", { className: "border border-gray-200 rounded-lg", children: [_jsx(FilePreviewList, { files: listUploadFiles, onRemove: handleRemoveFile }), _jsx("div", { className: "relative", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "px-3 py-2 text-sm outline-none", style: { minHeight } }), ErrorBoundary: LexicalErrorBoundary, placeholder: placeholder ? (_jsx("div", { className: "absolute top-2 left-3 pointer-events-none", children: _jsx("p", { className: "text-gray-400 text-sm", children: placeholder }) })) : null }) }), _jsxs("div", { className: "flex items-center border-t border-gray-100 px-2 py-1", children: [_jsx(ToolbarPlugin, {}), _jsx(MediaActions, { listUploadFiles: listUploadFiles, onFilesChange: handleFilesChange, showMediaUpload: showMediaUpload })] })] }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange, ignoreSelectionChange: true }), _jsx(HtmlPlugin, { initialHtml: initialHtml })] }));
62
+ }