@droppii-org/chat-sdk 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/assets/svg/cannedResponse.d.ts +7 -0
  2. package/dist/assets/svg/cannedResponse.d.ts.map +1 -0
  3. package/dist/assets/svg/cannedResponse.js +3 -0
  4. package/dist/assets/svg/cannedResponse.tsx +48 -0
  5. package/dist/assets/svg/index.d.ts +1 -0
  6. package/dist/assets/svg/index.d.ts.map +1 -1
  7. package/dist/assets/svg/index.js +1 -0
  8. package/dist/assets/svg/index.ts +1 -0
  9. package/dist/components/cannedResponse/CannedResponseBody.d.ts +8 -0
  10. package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -0
  11. package/dist/components/cannedResponse/CannedResponseBody.js +58 -0
  12. package/dist/components/cannedResponse/CannedResponseFooter.d.ts +6 -0
  13. package/dist/components/cannedResponse/CannedResponseFooter.d.ts.map +1 -0
  14. package/dist/components/cannedResponse/CannedResponseFooter.js +8 -0
  15. package/dist/components/cannedResponse/CannedResponseHeader.d.ts +8 -0
  16. package/dist/components/cannedResponse/CannedResponseHeader.d.ts.map +1 -0
  17. package/dist/components/cannedResponse/CannedResponseHeader.js +11 -0
  18. package/dist/components/cannedResponse/index.d.ts +8 -0
  19. package/dist/components/cannedResponse/index.d.ts.map +1 -0
  20. package/dist/components/cannedResponse/index.js +34 -0
  21. package/dist/components/cannedResponse/team/TeamItem.d.ts +11 -0
  22. package/dist/components/cannedResponse/team/TeamItem.d.ts.map +1 -0
  23. package/dist/components/cannedResponse/team/TeamItem.js +31 -0
  24. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  25. package/dist/components/conversation/ConversationBySessionItem.js +6 -2
  26. package/dist/components/mediaCollection/LinkCollection.js +1 -1
  27. package/dist/components/message/MediaPreviewIcon.d.ts +7 -0
  28. package/dist/components/message/MediaPreviewIcon.d.ts.map +1 -0
  29. package/dist/components/message/MediaPreviewIcon.js +24 -0
  30. package/dist/components/message/MessageHeader.js +1 -1
  31. package/dist/components/message/MessageList.d.ts +1 -0
  32. package/dist/components/message/MessageList.d.ts.map +1 -1
  33. package/dist/components/message/MessageList.js +49 -6
  34. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  35. package/dist/components/message/footer/ActionBar.js +15 -86
  36. package/dist/components/message/footer/CannedResponsePlugin.d.ts +7 -0
  37. package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -0
  38. package/dist/components/message/footer/CannedResponsePlugin.js +31 -0
  39. package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
  40. package/dist/components/message/footer/EmojiPicker.js +9 -5
  41. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  42. package/dist/components/message/footer/EnterHandler.js +16 -5
  43. package/dist/components/message/footer/FilePreview.d.ts +5 -0
  44. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  45. package/dist/components/message/footer/FilePreview.js +15 -12
  46. package/dist/components/message/footer/MediaActions.d.ts +10 -0
  47. package/dist/components/message/footer/MediaActions.d.ts.map +1 -0
  48. package/dist/components/message/footer/MediaActions.js +98 -0
  49. package/dist/components/message/footer/QuotedMessage.d.ts +2 -0
  50. package/dist/components/message/footer/QuotedMessage.d.ts.map +1 -0
  51. package/dist/components/message/footer/QuotedMessage.js +24 -0
  52. package/dist/components/message/footer/editorConfig.d.ts +24 -0
  53. package/dist/components/message/footer/editorConfig.d.ts.map +1 -0
  54. package/dist/components/message/footer/editorConfig.js +33 -0
  55. package/dist/components/message/footer/index.d.ts +2 -1
  56. package/dist/components/message/footer/index.d.ts.map +1 -1
  57. package/dist/components/message/footer/index.js +33 -28
  58. package/dist/components/message/item/QuoteMessage.d.ts +9 -0
  59. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -0
  60. package/dist/components/message/item/QuoteMessage.js +41 -0
  61. package/dist/components/message/item/RevokeMessage.d.ts +5 -0
  62. package/dist/components/message/item/RevokeMessage.d.ts.map +1 -0
  63. package/dist/components/message/item/RevokeMessage.js +8 -0
  64. package/dist/components/message/item/TextMessage.js +1 -1
  65. package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
  66. package/dist/components/message/item/UrlTextMessage.js +3 -3
  67. package/dist/components/message/item/index.d.ts +6 -1
  68. package/dist/components/message/item/index.d.ts.map +1 -1
  69. package/dist/components/message/item/index.js +79 -25
  70. package/dist/components/richTextEditor/RichTextEditor.d.ts +12 -0
  71. package/dist/components/richTextEditor/RichTextEditor.d.ts.map +1 -0
  72. package/dist/components/richTextEditor/RichTextEditor.js +62 -0
  73. package/dist/components/searchConversation/SearchDrawer.js +1 -1
  74. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts +3 -1
  75. package/dist/components/searchConversation/item/SearchItemAsMessage.d.ts.map +1 -1
  76. package/dist/components/searchConversation/item/SearchItemAsMessage.js +5 -2
  77. package/dist/components/thread/AssignConfirmModal.d.ts +12 -0
  78. package/dist/components/thread/AssignConfirmModal.d.ts.map +1 -0
  79. package/dist/components/thread/AssignConfirmModal.js +11 -0
  80. package/dist/components/thread/ManualAssignPopover.d.ts +14 -0
  81. package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -0
  82. package/dist/components/thread/ManualAssignPopover.js +83 -0
  83. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  84. package/dist/components/thread/SessionSection.js +11 -6
  85. package/dist/components/thread/UserSection.js +1 -1
  86. package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts +3 -0
  87. package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts.map +1 -0
  88. package/dist/hooks/cannedResponse/useFetchCannedCategories.js +13 -0
  89. package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts +219 -0
  90. package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts.map +1 -0
  91. package/dist/hooks/cannedResponse/useFetchCannedResponse.js +55 -0
  92. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  93. package/dist/hooks/message/useMessage.js +18 -1
  94. package/dist/hooks/message/useRevokeMessage.d.ts +5 -0
  95. package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -0
  96. package/dist/hooks/message/useRevokeMessage.js +16 -0
  97. package/dist/hooks/message/useSendMessage.d.ts +1 -0
  98. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  99. package/dist/hooks/message/useSendMessage.js +40 -9
  100. package/dist/hooks/session/useAssignSession.d.ts +8 -0
  101. package/dist/hooks/session/useAssignSession.d.ts.map +1 -0
  102. package/dist/hooks/session/useAssignSession.js +15 -0
  103. package/dist/hooks/session/useCreateNote.d.ts.map +1 -1
  104. package/dist/hooks/session/useCreateNote.js +2 -1
  105. package/dist/hooks/session/useGetTeamSupporters.d.ts +8 -0
  106. package/dist/hooks/session/useGetTeamSupporters.d.ts.map +1 -0
  107. package/dist/hooks/session/useGetTeamSupporters.js +20 -0
  108. package/dist/hooks/team/useFetchMyTeam.d.ts +3 -0
  109. package/dist/hooks/team/useFetchMyTeam.d.ts.map +1 -0
  110. package/dist/hooks/team/useFetchMyTeam.js +12 -0
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +1 -0
  114. package/dist/locales/vi/common.json +54 -53
  115. package/dist/screens/deskMessage/index.d.ts +4 -1
  116. package/dist/screens/deskMessage/index.d.ts.map +1 -1
  117. package/dist/screens/deskMessage/index.js +2 -2
  118. package/dist/services/query.d.ts +5 -0
  119. package/dist/services/query.d.ts.map +1 -1
  120. package/dist/services/query.js +5 -0
  121. package/dist/services/routes.d.ts +5 -0
  122. package/dist/services/routes.d.ts.map +1 -1
  123. package/dist/services/routes.js +5 -0
  124. package/dist/store/conversation.d.ts.map +1 -1
  125. package/dist/store/conversation.js +7 -1
  126. package/dist/styles/global.css +1 -1
  127. package/dist/tsconfig.tsbuildinfo +1 -0
  128. package/dist/types/chat.d.ts +7 -1
  129. package/dist/types/chat.d.ts.map +1 -1
  130. package/dist/types/chat.js +5 -0
  131. package/dist/types/dto.d.ts +53 -1
  132. package/dist/types/dto.d.ts.map +1 -1
  133. package/dist/utils/common.d.ts +3 -2
  134. package/dist/utils/common.d.ts.map +1 -1
  135. package/dist/utils/common.js +43 -19
  136. package/dist/utils/fileValidation.d.ts.map +1 -1
  137. package/dist/utils/fileValidation.js +2 -8
  138. package/dist/utils/queryHelpers.d.ts +3 -0
  139. package/dist/utils/queryHelpers.d.ts.map +1 -0
  140. package/dist/utils/queryHelpers.js +11 -0
  141. package/package.json +1 -1
@@ -7,52 +7,41 @@ 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";
16
14
  import { useSendMessage } from "../../../hooks/message/useSendMessage";
15
+ import { Popover } from "antd";
17
16
  import FilePreview from "./FilePreview";
18
17
  import { useTranslation } from "react-i18next";
19
18
  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
- };
19
+ import { QuotedMessageFooter } from "./QuotedMessage";
20
+ import CannedResponse from "../../cannedResponse";
21
+ import { CannedResponseTriggerPlugin } from "./CannedResponsePlugin";
22
+ import { useBoolean } from "ahooks";
23
+ import useAuthStore from "../../../store/auth";
40
24
  const initialConfig = {
41
25
  namespace: "ChatInput",
42
- theme,
43
- onError,
44
- nodes: [HeadingNode, ListNode, ListItemNode, QuoteNode, LinkNode],
26
+ theme: editorTheme,
27
+ onError: editorOnError,
28
+ nodes: editorNodes,
45
29
  };
46
30
  export const MessageFooterContext = createContext({
47
31
  onSendMessage: () => { },
48
32
  listUploadFiles: [],
49
33
  setListUploadFiles: () => { },
34
+ isOpenCanned: false,
35
+ setIsOpenCanned: () => { },
50
36
  });
51
37
  export const useMessageFooterContext = () => useContext(MessageFooterContext);
52
- const MessageFooterProvider = ({ currentSession }) => {
38
+ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
53
39
  const { t } = useTranslation();
54
40
  const { sendTextMessage, sendMergeMessage } = useSendMessage();
55
41
  const [listUploadFiles, setListUploadFiles] = useState([]);
42
+ const [isOpenCanned, { setTrue: openCanned, setFalse: closeCanned, toggle: toggleCanned },] = useBoolean(false);
43
+ const isCrm = useAuthStore((state) => state.isCrm);
44
+ const [cannedQuery, setCannedQuery] = useState("");
56
45
  const onSendMessage = useCallback(async ({ plainText, richText, type, }) => {
57
46
  if (type === "text") {
58
47
  sendTextMessage({ plainText, richText, currentSession });
@@ -67,6 +56,22 @@ const MessageFooterProvider = ({ currentSession }) => {
67
56
  }
68
57
  setListUploadFiles([]);
69
58
  }, [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, {})] }) }));
59
+ const onOpenCreateCannedModal = useCallback(() => {
60
+ closeCanned();
61
+ openCreateCannedModal === null || openCreateCannedModal === void 0 ? void 0 : openCreateCannedModal();
62
+ }, [closeCanned, openCreateCannedModal]);
63
+ const onOpenCanned = useCallback((query) => {
64
+ setCannedQuery(query);
65
+ openCanned();
66
+ }, [openCanned]);
67
+ return (_jsx(MessageFooterContext.Provider, { value: {
68
+ onSendMessage,
69
+ listUploadFiles,
70
+ setListUploadFiles,
71
+ isOpenCanned,
72
+ setIsOpenCanned: toggleCanned,
73
+ }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Popover, { open: isOpenCanned, content: _jsx(CannedResponse, { onClose: closeCanned, openCreateCannedModal: onOpenCreateCannedModal, cannedQuery: cannedQuery }), placement: "topLeft", trigger: "click", arrow: false, classNames: {
74
+ body: "mx-1 !p-0",
75
+ }, destroyOnHidden: true, 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, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }) }));
71
76
  };
72
77
  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;AAUnE,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,4CA+DvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,41 @@
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
+ import useAuthStore from "../../../store/auth";
8
+ import { useMemo } from "react";
9
+ import useConversationStore from "../../../store/conversation";
10
+ const QuoteMessageItem = ({ message, isMine, onPressQuoteMessage, }) => {
11
+ var _a, _b, _c, _d, _e, _f;
12
+ const userID = useAuthStore((state) => state.userID);
13
+ const isCrm = useAuthStore((state) => state.isCrm);
14
+ const conversationData = useConversationStore((state) => state.conversationData);
15
+ const { t } = useTranslation();
16
+ const { from, to } = useMemo(() => {
17
+ var _a, _b, _c, _d, _e, _f, _g, _h;
18
+ const isChatSupport = (_d = (_c = (_b = (_a = JSON === null || JSON === void 0 ? void 0 : JSON.parse) === null || _a === void 0 ? void 0 : _a.call(JSON, (conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _b === void 0 ? void 0 : _b.sessionInfo) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.botId;
19
+ const isQuoteFromYourself = ((_f = (_e = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _e === void 0 ? void 0 : _e.quoteMessage) === null || _f === void 0 ? void 0 : _f.sendID) === (message === null || message === void 0 ? void 0 : message.sendID);
20
+ return {
21
+ from: isChatSupport && !isCrm
22
+ ? isMine
23
+ ? t("you")
24
+ : conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName
25
+ : message === null || message === void 0 ? void 0 : message.senderNickname,
26
+ to: isQuoteFromYourself
27
+ ? t("your_self")
28
+ : isChatSupport && !isCrm
29
+ ? conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName
30
+ : (_h = (_g = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _g === void 0 ? void 0 : _g.quoteMessage) === null || _h === void 0 ? void 0 : _h.senderNickname,
31
+ };
32
+ }, [isMine, message, t, conversationData, userID]);
33
+ return (_jsxs("div", { className: clsx("mb-[-4px] flex flex-col gap-1 flex-1 w-full", isMine ? "items-end" : "items-start"), children: [_jsxs("div", { className: "flex flex-row flex-1 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", {
34
+ from: from,
35
+ to: to,
36
+ }) })] }), _jsxs("div", { className: "flex flex-row flex-1 max-w-full items-center gap-2 border border-gray-200 rounded-2xl min-w-0 px-3 py-2 bg-gray-100 cursor-pointer", onClick: () => {
37
+ var _a, _b;
38
+ 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) || "");
39
+ }, children: [_jsx(MediaPreviewIcon, { message: (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage }), _jsx("span", { className: "text-sm text-gray-500 truncate", children: generateContentBasedOnMessageType((_c = (_b = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _b === void 0 ? void 0 : _b.quoteMessage) === null || _c === void 0 ? void 0 : _c.contentType, ((_f = (_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.textElem) === null || _f === void 0 ? void 0 : _f.content) || "") })] })] }));
40
+ };
41
+ 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 max-w-[75%] 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 w-full", 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
+ }
@@ -26,7 +26,7 @@ const SearchDrawer = () => {
26
26
  padding: 0,
27
27
  height: "100%",
28
28
  },
29
- }, getContainer: false, width: isMobile ? "100%" : 360, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsx("span", { className: "text-lg font-medium", children: "T\u00ECm ki\u1EBFm tin nh\u1EAFn" }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "close-b", size: 22 }) })] }), _jsx("div", { className: "px-3", children: _jsx(Input, { ref: searchInputRef, placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), onChange: (e) => {
29
+ }, getContainer: false, width: isMobile ? "100%" : 360, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsx("span", { className: "text-lg font-medium", children: t("search_message_title") }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "close-b", size: 22 }) })] }), _jsx("div", { className: "px-3", children: _jsx(Input, { ref: searchInputRef, placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), onChange: (e) => {
30
30
  setSearch(e.target.value);
31
31
  }, className: "rounded-lg text-sm", size: "large", allowClear: true, value: search, autoFocus: false }) }), _jsx("div", { className: "flex-1 py-3 overflow-auto", children: _jsx(SearchMessageOnCurrentConversation, { searchTerm: search, onClose: toggle }) })] }) })] }));
32
32
  };
@@ -1,6 +1,8 @@
1
1
  import { MessageItem } from "@openim/wasm-client-sdk";
2
2
  interface SearchItemAsMessageProps {
3
- message: MessageItem;
3
+ message: MessageItem & {
4
+ isRevoked?: boolean;
5
+ };
4
6
  searchTerm: string;
5
7
  onClick?: () => void;
6
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SearchItemAsMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/searchConversation/item/SearchItemAsMessage.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAWnE,UAAU,wBAAwB;IAChC,OAAO,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,mBAAmB,GAAI,OAAO,wBAAwB,4CA+F3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"SearchItemAsMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/searchConversation/item/SearchItemAsMessage.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAA4B,MAAM,yBAAyB,CAAC;AAehF,UAAU,wBAAwB;IAChC,OAAO,EAAE,WAAW,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,mBAAmB,GAAI,OAAO,wBAAwB,4CAkG3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { SessionType } from "@openim/wasm-client-sdk";
4
4
  import { Avatar } from "antd";
5
- import { formatTimestamp, highlightSearch, sanitizeHtml } from "../../../utils/common";
5
+ import { formatTimestamp, highlightSearch, sanitizeHtml, } from "../../../utils/common";
6
6
  import { DChatSDK } from "../../../constants/sdk";
7
7
  import { useChatContext } from "../../../context/ChatContext";
8
8
  import { message as antdMessage } from "antd";
@@ -44,7 +44,10 @@ const SearchItemAsMessage = (props) => {
44
44
  };
45
45
  let msgContent = "";
46
46
  try {
47
- msgContent = ((_a = JSON.parse((message === null || message === void 0 ? void 0 : message.content) || "{}")) === null || _a === void 0 ? void 0 : _a.content) || "";
47
+ msgContent =
48
+ (message === null || message === void 0 ? void 0 : message.isRevoked) === true
49
+ ? t("revoked")
50
+ : ((_a = JSON.parse((message === null || message === void 0 ? void 0 : message.content) || "{}")) === null || _a === void 0 ? void 0 : _a.content) || "";
48
51
  }
49
52
  catch (error) {
50
53
  console.error("Failed to parse message content", error);
@@ -0,0 +1,12 @@
1
+ import { ISupporterResponse } from "../../types/dto";
2
+ interface AssignConfirmModalProps {
3
+ open: boolean;
4
+ agent: ISupporterResponse | null;
5
+ loading: boolean;
6
+ onCancel: () => void;
7
+ onConfirm: () => void;
8
+ afterClose?: () => void;
9
+ }
10
+ declare const AssignConfirmModal: ({ open, agent, loading, onCancel, onConfirm, afterClose, }: AssignConfirmModalProps) => import("react/jsx-runtime").JSX.Element;
11
+ export default AssignConfirmModal;
12
+ //# sourceMappingURL=AssignConfirmModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssignConfirmModal.d.ts","sourceRoot":"","sources":["../../../src/components/thread/AssignConfirmModal.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrD,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,QAAA,MAAM,kBAAkB,GAAI,4DAOzB,uBAAuB,4CAwDzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal, Button, Avatar } from "antd";
3
+ import { useTranslation, Trans } from "react-i18next";
4
+ const AssignConfirmModal = ({ open, agent, loading, onCancel, onConfirm, afterClose, }) => {
5
+ var _a;
6
+ const { t } = useTranslation();
7
+ return (_jsx(Modal, { open: open, onCancel: onCancel, footer: null, closable: !loading, centered: true, forceRender: true, width: 400, maskClosable: !loading, keyboard: !loading, afterClose: afterClose, children: _jsxs("div", { className: "flex flex-col items-center gap-4 pt-2", role: "alertdialog", "aria-label": t("manual_assign_confirm_question", {
8
+ name: agent === null || agent === void 0 ? void 0 : agent.displayName,
9
+ }), children: [_jsx(Avatar, { src: agent === null || agent === void 0 ? void 0 : agent.avatar, size: 48, children: ((_a = agent === null || agent === void 0 ? void 0 : agent.displayName) === null || _a === void 0 ? void 0 : _a.charAt(0)) || "A" }), _jsx("p", { className: "text-base text-center", children: _jsx(Trans, { i18nKey: "manual_assign_confirm_question", values: { name: agent === null || agent === void 0 ? void 0 : agent.displayName }, components: { bold: _jsx("strong", {}) } }) }), _jsxs("div", { className: "flex gap-3 w-full", children: [_jsx(Button, { className: "flex-1", size: "large", onClick: onCancel, disabled: loading, children: t("cancel") }), _jsx(Button, { type: "primary", className: "flex-1", size: "large", onClick: onConfirm, loading: loading, disabled: !agent, children: t("manual_assign_button_reassign") })] })] }) }));
10
+ };
11
+ export default AssignConfirmModal;
@@ -0,0 +1,14 @@
1
+ interface SupporterInfo {
2
+ avatar: string;
3
+ username: string;
4
+ fullName: string;
5
+ id: string;
6
+ }
7
+ interface ManualAssignPopoverProps {
8
+ sessionId: string;
9
+ supporter: SupporterInfo;
10
+ teamId?: string;
11
+ }
12
+ declare const ManualAssignPopover: ({ sessionId, supporter, teamId: propTeamId, }: ManualAssignPopoverProps) => import("react/jsx-runtime").JSX.Element;
13
+ export default ManualAssignPopover;
14
+ //# sourceMappingURL=ManualAssignPopover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManualAssignPopover.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ManualAssignPopover.tsx"],"names":[],"mappings":"AAgBA,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,UAAU,wBAAwB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA0CD,QAAA,MAAM,mBAAmB,GAAI,+CAI1B,wBAAwB,4CA8J1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}