@droppii-org/chat-sdk 0.1.56 → 0.1.58

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 (45) hide show
  1. package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
  2. package/dist/components/conversation-detail/ConversationDetailInput.js +2 -1
  3. package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
  4. package/dist/components/conversation-detail/ConversationMessageList.js +26 -5
  5. package/dist/components/message/MessageList.d.ts.map +1 -1
  6. package/dist/components/message/MessageList.js +1 -23
  7. package/dist/components/message/footer/index.js +2 -2
  8. package/dist/components/message/item/QuoteMessage.d.ts +1 -2
  9. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
  10. package/dist/components/message/item/QuoteMessage.js +13 -49
  11. package/dist/components/message/item/index.d.ts +2 -1
  12. package/dist/components/message/item/index.d.ts.map +1 -1
  13. package/dist/components/message/item/index.js +54 -41
  14. package/dist/components/message/reply/ReplyPreviewBar.d.ts +3 -0
  15. package/dist/components/message/reply/ReplyPreviewBar.d.ts.map +1 -0
  16. package/dist/components/message/reply/ReplyPreviewBar.js +23 -0
  17. package/dist/components/message/reply/ReplyPreviewMedia.d.ts +7 -0
  18. package/dist/components/message/reply/ReplyPreviewMedia.d.ts.map +1 -0
  19. package/dist/components/message/reply/ReplyPreviewMedia.js +41 -0
  20. package/dist/components/message/reply/ReplyQuoteContent.d.ts +9 -0
  21. package/dist/components/message/reply/ReplyQuoteContent.d.ts.map +1 -0
  22. package/dist/components/message/reply/ReplyQuoteContent.js +17 -0
  23. package/dist/components/message/toolbox/MessageToolboxMenu.d.ts +5 -0
  24. package/dist/components/message/toolbox/MessageToolboxMenu.d.ts.map +1 -0
  25. package/dist/components/message/toolbox/MessageToolboxMenu.js +10 -0
  26. package/dist/components/message/toolbox/getCopyableMessageText.d.ts +4 -0
  27. package/dist/components/message/toolbox/getCopyableMessageText.d.ts.map +1 -0
  28. package/dist/components/message/toolbox/getCopyableMessageText.js +28 -0
  29. package/dist/components/message/toolbox/index.d.ts +5 -0
  30. package/dist/components/message/toolbox/index.d.ts.map +1 -0
  31. package/dist/components/message/toolbox/index.js +4 -0
  32. package/dist/components/message/toolbox/types.d.ts +15 -0
  33. package/dist/components/message/toolbox/types.d.ts.map +1 -0
  34. package/dist/components/message/toolbox/types.js +6 -0
  35. package/dist/components/message/toolbox/useMessageToolboxVisibility.d.ts +9 -0
  36. package/dist/components/message/toolbox/useMessageToolboxVisibility.d.ts.map +1 -0
  37. package/dist/components/message/toolbox/useMessageToolboxVisibility.js +15 -0
  38. package/dist/hooks/message/useMessageListScroll.d.ts +1 -0
  39. package/dist/hooks/message/useMessageListScroll.d.ts.map +1 -1
  40. package/dist/hooks/message/useMessageListScroll.js +22 -0
  41. package/dist/locales/vi/common.json +8 -4
  42. package/dist/styles/global.css +1 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/dist/utils/common.js +2 -2
  45. package/package.json +126 -127
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,uBAAuB,GAAI,gDAI9B,4BAA4B,4CA2Y9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AA+BA,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,uBAAuB,GAAI,gDAI9B,4BAA4B,4CA6Y9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -16,6 +16,7 @@ import { useFetchUrlMetadata } from "../../hooks/common/useFetchUrlMetadata";
16
16
  import { processAndValidateDocuments, processAndValidateFiles, } from "../../utils/fileValidation";
17
17
  import { FilePreviewList } from "../../components/message/footer/FilePreview";
18
18
  import LinkInputPreview from "../../components/message/footer/LinkInputPreview";
19
+ import { ReplyPreviewBar } from "../../components/message/reply/ReplyPreviewBar";
19
20
  import ConversationMoreOptionPanel from "./ConversationMoreOptionPanel";
20
21
  const MAX_LINES = 5;
21
22
  const CONTAINER_MIN_HEIGHT = 48;
@@ -251,7 +252,7 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, })
251
252
  event.preventDefault();
252
253
  void handleSend();
253
254
  };
254
- return (_jsxs("div", { ref: containerRef, "data-testid": "conversation-detail-input", className: "flex w-full shrink-0 flex-col bg-chat-bubble-neutral/[0.05]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), _jsx("input", { ref: documentInputRef, type: "file", accept: ".pdf,.doc,.docx", multiple: true, className: "hidden", onChange: handleDocumentInputChange }), listUploadFiles.length > 0 && (_jsx(FilePreviewList, { files: listUploadFiles, onRemove: (file) => handleRemoveFile(file.uid), onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "conversation-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-o", size: 24, className: "text-primary-400" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-gray-900" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
255
+ return (_jsxs("div", { ref: containerRef, "data-testid": "conversation-detail-input", className: "flex w-full shrink-0 flex-col bg-chat-bubble-neutral/[0.05]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), _jsx("input", { ref: documentInputRef, type: "file", accept: ".pdf,.doc,.docx", multiple: true, className: "hidden", onChange: handleDocumentInputChange }), _jsx(ReplyPreviewBar, {}), listUploadFiles.length > 0 && (_jsx(FilePreviewList, { files: listUploadFiles, onRemove: (file) => handleRemoveFile(file.uid), onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "conversation-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-o", size: 24, className: "text-primary-400" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-gray-900" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
255
256
  ? "border-primary-200 shadow-primary-focus"
256
257
  : "border-gray-50"), style: { minHeight: CONTAINER_MIN_HEIGHT }, children: _jsx("div", { className: "flex min-w-0 flex-[1_0_0] items-center gap-2 self-stretch px-3 py-1", children: _jsx("textarea", { ref: textareaRef, value: value, rows: 1, onChange: (event) => handleChange(event.target.value), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "conversation-detail-message-input", className: "conversation-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-gray-400 focus:border-0 focus:outline-none focus:ring-0" }) }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "conversation-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-chat-send" : "bg-chat-send opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(ConversationMoreOptionPanel, { disabled: !isConversationReady, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, onTriggerDocumentInput: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
257
258
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAalD,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,gBAAgB,4BAA4B,4CA0E5E,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAczE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,gBAAgB,4BAA4B,4CAsH5E,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect } from "react";
3
+ import { useCallback, useEffect, useState } from "react";
4
4
  import { Empty, Spin } from "antd";
5
+ import { useEventListener } from "ahooks";
5
6
  import { useTranslation } from "react-i18next";
6
7
  import { useChatContext } from "../../context/ChatContext";
7
8
  import MessageInfiniteScroll from "../../components/message/MessageInfiniteScroll";
@@ -18,18 +19,38 @@ const ConversationMessageList = ({ bottomSlot }) => {
18
19
  const authUserID = useAuthStore((state) => state.userID);
19
20
  const conversation = useConversationStore((state) => state.conversationData);
20
21
  const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
22
+ const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
23
+ const searchClientMsgID = useConversationStore((state) => state.searchClientMsgID);
24
+ const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
21
25
  const conversationId = selectedConversationId || (conversation === null || conversation === void 0 ? void 0 : conversation.conversationID) || "";
22
26
  const isGroupChat = conversation ? resolveIsGroupChat(conversation) : false;
23
- const messageController = useMessage(conversationId);
24
- const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottomIfAtBottom, } = useMessageListScroll(Object.assign({ conversationId, userId: (user === null || user === void 0 ? void 0 : user.userID) || authUserID }, messageController));
27
+ const messageController = useMessage(conversationId, searchClientMsgID);
28
+ const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottomIfAtBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: (user === null || user === void 0 ? void 0 : user.userID) || authUserID }, messageController));
25
29
  const { loadState, moreOldLoading } = messageController;
26
30
  const isMessageLoading = !conversationId || loadState.initLoading;
31
+ const [openToolboxMessageId, setOpenToolboxMessageId] = useState(null);
32
+ const closeToolbox = useCallback(() => setOpenToolboxMessageId(null), []);
33
+ const onPressQuoteMessage = useCallback((clientMsgID) => {
34
+ const hasMessage = loadState.messageList.some((msg) => msg.clientMsgID === clientMsgID);
35
+ if (hasMessage) {
36
+ scrollToMessage(clientMsgID);
37
+ }
38
+ else {
39
+ setSearchClientMsgID(clientMsgID);
40
+ }
41
+ }, [scrollToMessage, loadState.messageList, setSearchClientMsgID]);
27
42
  useEffect(() => {
28
43
  emitter.on("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM", scrollToBottomIfAtBottom);
44
+ emitter.on("CHAT_LIST_SCROLL_TO_MESSAGE", scrollToMessage);
29
45
  return () => {
30
46
  emitter.off("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM", scrollToBottomIfAtBottom);
47
+ emitter.off("CHAT_LIST_SCROLL_TO_MESSAGE", scrollToMessage);
31
48
  };
32
- }, [scrollToBottomIfAtBottom]);
33
- return (_jsx("div", { "data-testid": "conversation-message-list", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [bottomSlot, loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot }, message.clientMsgID)))] })) }));
49
+ }, [scrollToBottomIfAtBottom, scrollToMessage]);
50
+ useEventListener("scroll", closeToolbox, {
51
+ target: scrollRef,
52
+ enable: !!openToolboxMessageId,
53
+ });
54
+ return (_jsx("div", { "data-testid": "conversation-message-list", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [bottomSlot, loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: setQuotedMessage, onRevokeMessage: closeToolbox, onPressQuoteMessage: onPressQuoteMessage, enableLongPress: true }, message.clientMsgID)))] })) }));
34
55
  };
35
56
  export default ConversationMessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AA0BA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAgQ3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAyBA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAgO3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -15,7 +15,6 @@ import { useTranslation } from "react-i18next";
15
15
  import useConversationStore from "../../store/conversation";
16
16
  import { useBoolean } from "ahooks";
17
17
  import { useMessageListScroll } from "../../hooks/message/useMessageListScroll";
18
- import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../constants";
19
18
  import { useChatContext } from "../../context/ChatContext";
20
19
  import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
21
20
  import { useConversationSessionState } from "../../hooks/session/useConversationSessionState";
@@ -49,7 +48,7 @@ const MessageList = (props) => {
49
48
  customerUserID,
50
49
  });
51
50
  const { loadState, moreOldLoading, moreNewLoading } = messageController;
52
- const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottom } = useMessageListScroll(Object.assign({ conversationId, userId: user === null || user === void 0 ? void 0 : user.userID }, messageController));
51
+ const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: user === null || user === void 0 ? void 0 : user.userID }, messageController));
53
52
  const handleOpenRevoke = useCallback((clientMsgID) => {
54
53
  setSelectedItem(clientMsgID);
55
54
  openConfirmRevoke();
@@ -67,27 +66,6 @@ const MessageList = (props) => {
67
66
  revokeMessage,
68
67
  handleCloseRevoke,
69
68
  ]);
70
- const scrollToMessage = (clientMsgID) => {
71
- setTimeout(() => {
72
- const targetElement = document.getElementById(`${MSG_ITEM_PREFIX}${clientMsgID}`);
73
- targetElement === null || targetElement === void 0 ? void 0 : targetElement.scrollIntoView({
74
- behavior: "auto",
75
- block: "center",
76
- });
77
- setTimeout(() => {
78
- const targetContentElement = document.getElementById(`${MSG_ITEM_CONTENT_PREFIX}${clientMsgID}`);
79
- targetContentElement === null || targetContentElement === void 0 ? void 0 : targetContentElement.classList.add("zoom-in-out-element", "border-blue-500", "border");
80
- // Khi animation kết thúc thì remove element
81
- const onEnd = () => {
82
- targetContentElement === null || targetContentElement === void 0 ? void 0 : targetContentElement.classList.remove("zoom-in-out-element", "border-blue-500", "border");
83
- targetContentElement === null || targetContentElement === void 0 ? void 0 : targetContentElement.removeEventListener("animationend", onEnd);
84
- };
85
- targetContentElement === null || targetContentElement === void 0 ? void 0 : targetContentElement.addEventListener("animationend", onEnd, {
86
- once: true,
87
- });
88
- }, 500);
89
- }, 200);
90
- };
91
69
  const onPressQuoteMessage = useCallback((clientMsgID) => {
92
70
  const hasMessage = loadState.messageList.some((msg) => msg.clientMsgID === clientMsgID);
93
71
  if (hasMessage) {
@@ -18,7 +18,7 @@ import { extractLinkUrls } from "../../../utils/link";
18
18
  import { useFetchUrlMetadata } from "../../../hooks/common/useFetchUrlMetadata";
19
19
  import { useTranslation } from "react-i18next";
20
20
  import PasteAndDropPlugin from "./PasteAndDropPlugin";
21
- import { QuotedMessageFooter } from "./QuotedMessage";
21
+ import { ReplyPreviewBar } from "../../../components/message/reply/ReplyPreviewBar";
22
22
  import CannedResponse from "../../../components/canned-response";
23
23
  import { CannedResponseTriggerPlugin } from "./CannedResponsePlugin";
24
24
  import { useBoolean } from "ahooks";
@@ -214,6 +214,6 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
214
214
  setMaxActiveCannedIndex,
215
215
  }, 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: {
216
216
  body: "mx-1 !p-0",
217
- }, 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, {}), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setIsLinkPreviewDismissed(true) })), _jsx(ToolbarPlugin, {}), _jsxs("div", { children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, hintText: t("canned_response_hint"), isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), _jsx(ComposerEditor, { isInternal: isInternal, placeholderText: placeholderText })] }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(ComposerTextSyncPlugin, { onChange: setComposerText }), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }), _jsx(SessionClosedModal, { open: isSessionClosedModalOpen, loading: isSessionActionLoading, onReopen: handleReopenSession, onCreateNew: handleCreateNewSession, onClose: handleCloseSessionModal })] }));
217
+ }, destroyOnHidden: true, children: _jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(ReplyPreviewBar, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setIsLinkPreviewDismissed(true) })), _jsx(ToolbarPlugin, {}), _jsxs("div", { children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, hintText: t("canned_response_hint"), isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), _jsx(ComposerEditor, { isInternal: isInternal, placeholderText: placeholderText })] }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(ComposerTextSyncPlugin, { onChange: setComposerText }), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }), _jsx(SessionClosedModal, { open: isSessionClosedModalOpen, loading: isSessionActionLoading, onReopen: handleReopenSession, onCreateNew: handleCreateNewSession, onClose: handleCloseSessionModal })] }));
218
218
  };
219
219
  export default MessageFooterProvider;
@@ -1,9 +1,8 @@
1
1
  import { MessageItem } from "@openim/wasm-client-sdk";
2
2
  interface QuoteMessageItemProps {
3
3
  message: MessageItem;
4
- isMine: boolean;
5
4
  onPressQuoteMessage?: (clientMsgID: string) => void;
6
5
  }
7
- declare const QuoteMessageItem: ({ message, isMine, onPressQuoteMessage, }: QuoteMessageItemProps) => import("react/jsx-runtime").JSX.Element;
6
+ declare const QuoteMessageItem: ({ message, onPressQuoteMessage, }: QuoteMessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
8
7
  export default QuoteMessageItem;
9
8
  //# sourceMappingURL=QuoteMessage.d.ts.map
@@ -1 +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;AAKD,QAAA,MAAM,gBAAgB,GAAI,2CAIvB,qBAAqB,4CA0GvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
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;AAOtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,gBAAgB,GAAI,mCAGvB,qBAAqB,mDAkDvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,15 +1,11 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { generateContentBasedOnMessageType } from "../../../utils/common";
3
- import { Icon } from "../../../components/icon";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
4
2
  import { useTranslation } from "react-i18next";
5
- import clsx from "clsx";
6
- import MediaPreviewIcon from "../../../components/message/MediaPreviewIcon";
7
- import useAuthStore from "../../../store/auth";
8
3
  import { useCallback, useMemo } from "react";
4
+ import useAuthStore from "../../../store/auth";
9
5
  import useConversationStore from "../../../store/conversation";
10
- const capitalize = (text) => text ? text.charAt(0).toUpperCase() + text.slice(1) : "";
11
- const QuoteMessageItem = ({ message, isMine, onPressQuoteMessage, }) => {
12
- var _a, _b, _c, _d, _e, _f;
6
+ import ReplyQuoteContent from "../../../components/message/reply/ReplyQuoteContent";
7
+ const QuoteMessageItem = ({ message, onPressQuoteMessage, }) => {
8
+ var _a;
13
9
  const userID = useAuthStore((state) => state.userID);
14
10
  const isCrm = useAuthStore((state) => state.isCrm);
15
11
  const conversationData = useConversationStore((state) => state.conversationData);
@@ -33,45 +29,13 @@ const QuoteMessageItem = ({ message, isMine, onPressQuoteMessage, }) => {
33
29
  return t("you");
34
30
  return name;
35
31
  }, [isCrm, isChatSupport, conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName, userID, t]);
36
- const parseReplyText = useCallback(() => {
37
- var _a;
38
- const quoteMsg = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage;
39
- if (!quoteMsg)
40
- return "";
41
- const senderID = message === null || message === void 0 ? void 0 : message.sendID;
42
- const targetID = quoteMsg === null || quoteMsg === void 0 ? void 0 : quoteMsg.sendID;
43
- const senderName = message === null || message === void 0 ? void 0 : message.senderNickname;
44
- const targetName = quoteMsg === null || quoteMsg === void 0 ? void 0 : quoteMsg.senderNickname;
45
- const isSelfReply = senderID === targetID;
46
- const isFromMe = senderID === userID;
47
- const isReplyToMe = targetID === userID;
48
- const senderDisplay = getDisplayName({ name: senderName, id: senderID });
49
- const targetDisplay = getDisplayName({ name: targetName, id: targetID });
50
- if (isSelfReply) {
51
- return isFromMe
52
- ? capitalize(t("replied_yourself"))
53
- : `${senderDisplay} ${t("replied_yourself")}`;
54
- }
55
- if (isFromMe) {
56
- return `${capitalize(t("replied"))} ${targetDisplay}`;
57
- }
58
- if (isReplyToMe) {
59
- return `${senderDisplay} ${t("replied_you")}`;
60
- }
61
- return `${senderDisplay} ${t("replied")} ${targetDisplay}`;
62
- }, [
63
- message,
64
- userID,
65
- isCrm,
66
- isChatSupport,
67
- conversationData,
68
- t,
69
- getDisplayName,
70
- ]);
71
- const replyText = useMemo(() => parseReplyText(), [parseReplyText]);
72
- 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: replyText })] }), _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: () => {
73
- var _a, _b;
74
- 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) || "");
75
- }, 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) || "") })] })] }));
32
+ const quoteMessage = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage;
33
+ if (!quoteMessage)
34
+ return null;
35
+ const title = getDisplayName({
36
+ name: quoteMessage.senderNickname,
37
+ id: quoteMessage.sendID,
38
+ });
39
+ return (_jsx("div", { className: "flex cursor-pointer items-center overflow-hidden rounded-r-xl bg-white/70", onClick: () => onPressQuoteMessage === null || onPressQuoteMessage === void 0 ? void 0 : onPressQuoteMessage(quoteMessage.clientMsgID || ""), children: _jsx(ReplyQuoteContent, { message: quoteMessage, title: title || "" }) }));
76
40
  };
77
41
  export default QuoteMessageItem;
@@ -9,7 +9,8 @@ interface MessageItemProps {
9
9
  onRevokeMessage?: (clientMsgID: string) => void;
10
10
  onQuoteMessage?: (message: MessageItemType) => void;
11
11
  onPressQuoteMessage?: (clientMsgID: string) => void;
12
+ enableLongPress?: boolean;
12
13
  }
13
- declare const MessageItem: ({ message, allMessages, isGroupChat, forceHideTip, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
14
+ declare const MessageItem: ({ message, allMessages, isGroupChat, forceHideTip, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, enableLongPress, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
14
15
  export default MessageItem;
15
16
  //# 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;AA6BjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,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,sJAUlB,gBAAgB,mDA+QlB,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;AAuCjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,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;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAeD,QAAA,MAAM,WAAW,GAAI,uKAWlB,gBAAgB,mDA4RlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import { createElement as _createElement } from "react";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import dayjs from "dayjs";
4
4
  import clsx from "clsx";
5
- import { Dropdown } from "antd";
5
+ import { Dropdown, message as toastMessage } from "antd";
6
6
  import { MessageStatus, MessageType, } from "@openim/wasm-client-sdk";
7
7
  import TextMessageItem from "./TextMessage";
8
8
  import ImageMessageItem from "./ImageMessage";
@@ -15,7 +15,8 @@ import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../../constants";
15
15
  import { formatTimestamp } from "../../../utils/common";
16
16
  import useAuthStore from "../../../store/auth";
17
17
  import useConversationStore from "../../../store/conversation";
18
- import { useCallback, useMemo } from "react";
18
+ import { useCallback, useMemo, useRef } from "react";
19
+ import { useLongPress } from "ahooks";
19
20
  import { Trans, useTranslation } from "react-i18next";
20
21
  import UrlTextMessageItem from "./UrlTextMessage";
21
22
  import MessageStatusIndicator from "./MessageStatusIndicator";
@@ -25,8 +26,9 @@ import RevokeMessageItem from "./RevokeMessage";
25
26
  import QuoteMessageItem from "./QuoteMessage";
26
27
  import SystemLogMessageItem from "./SystemLogMessage";
27
28
  import { isSystemLogMessageType } from "../../../utils/messageLog";
29
+ import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessageType, MessageToolboxActionKey, MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, useMessageToolboxVisibility, } from "../../../components/message/toolbox";
28
30
  const MessageAvatar = ({ avatarUrl }) => avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "", className: "h-6 w-6 shrink-0 self-end rounded-full object-cover" })) : (_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center self-end rounded-full bg-chat-avatar", children: _jsx(Icon, { icon: "droppii-o-1", size: 14, className: "text-white" }) }));
29
- const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip = false, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, }) => {
31
+ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip = false, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, enableLongPress = false, }) => {
30
32
  var _a;
31
33
  const { t } = useTranslation();
32
34
  const { resendMessage } = useSendMessage();
@@ -56,52 +58,63 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
56
58
  return _jsx(TextMessageItem, { message: item });
57
59
  }
58
60
  }, []);
59
- const { isReplyable, isRevocable, isMine } = useMemo(() => {
60
- const isMyMessage = (message === null || message === void 0 ? void 0 : message.sendID) === userID;
61
- const isOlderThanOneDay = dayjs().diff(dayjs(message === null || message === void 0 ? void 0 : message.sendTime), "days", true) >= 1;
62
- const status = message.status;
63
- const isDeliveredForUi = status !== MessageStatus.Sending && status !== MessageStatus.Failed;
64
- return {
65
- isReplyable: isDeliveredForUi,
66
- isRevocable: isMyMessage && isDeliveredForUi && !isOlderThanOneDay,
67
- isMine: isMyMessage,
68
- };
69
- }, [userID, message]);
61
+ const { isDeliveredForUi, isRevocable, isMine } = useMessageToolboxVisibility(message, userID);
70
62
  const contextMenuItems = useMemo(() => {
71
63
  if ((message === null || message === void 0 ? void 0 : message.contentType) === MessageType.RevokeMessage ||
72
64
  isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType)) {
73
65
  return [];
74
66
  }
75
- return [
76
- ...(isReplyable && onQuoteMessage
77
- ? [
78
- {
79
- key: "TB01",
80
- label: t("context_menu_reply"),
81
- icon: (_jsx(Icon, { icon: "arrow-reply-alt-o", size: 18, className: "text-chat-focus" })),
82
- onClick: () => onQuoteMessage === null || onQuoteMessage === void 0 ? void 0 : onQuoteMessage(message),
83
- },
84
- ]
85
- : []),
86
- ...(isRevocable && onRevokeMessage
87
- ? [
88
- {
89
- key: "TB05",
90
- label: t("context_menu_revoke"),
91
- icon: (_jsx(Icon, { icon: "arrow-reload-o", size: 18, className: "text-chat-focus" })),
92
- onClick: () => onRevokeMessage === null || onRevokeMessage === void 0 ? void 0 : onRevokeMessage((message === null || message === void 0 ? void 0 : message.clientMsgID) || ""),
93
- },
94
- ]
95
- : []),
96
- ];
67
+ const closeToolbox = () => onContextMenuOpenChange === null || onContextMenuOpenChange === void 0 ? void 0 : onContextMenuOpenChange(false);
68
+ const actions = [];
69
+ if (isDeliveredForUi && onQuoteMessage) {
70
+ actions.push({
71
+ key: MessageToolboxActionKey.REPLY,
72
+ label: t("context_menu_reply"),
73
+ icon: "arrow-reply-o",
74
+ onClick: () => onQuoteMessage(message),
75
+ });
76
+ }
77
+ if (isDeliveredForUi) {
78
+ actions.push({
79
+ key: MessageToolboxActionKey.PIN,
80
+ label: t("context_menu_pin"),
81
+ icon: "thumbstack-o",
82
+ onClick: closeToolbox,
83
+ });
84
+ }
85
+ if (isDeliveredForUi && isCopyableMessageType(message === null || message === void 0 ? void 0 : message.contentType)) {
86
+ actions.push({
87
+ key: MessageToolboxActionKey.COPY_MESSAGE,
88
+ label: t("context_menu_copy_message"),
89
+ icon: "copy-o",
90
+ onClick: () => {
91
+ navigator.clipboard.writeText(getCopyableMessageText(message));
92
+ toastMessage.success(t("copy_message_success"));
93
+ closeToolbox();
94
+ },
95
+ });
96
+ }
97
+ if (isRevocable && onRevokeMessage) {
98
+ actions.push({
99
+ key: MessageToolboxActionKey.REVOKE,
100
+ label: t("context_menu_revoke"),
101
+ icon: "trash-alt-o-1",
102
+ danger: true,
103
+ onClick: () => onRevokeMessage((message === null || message === void 0 ? void 0 : message.clientMsgID) || ""),
104
+ });
105
+ }
106
+ return buildMessageToolboxMenuItems(actions);
97
107
  }, [
98
- isReplyable,
108
+ isDeliveredForUi,
99
109
  isRevocable,
100
110
  t,
101
111
  onRevokeMessage,
102
112
  message,
103
113
  onQuoteMessage,
114
+ onContextMenuOpenChange,
104
115
  ]);
116
+ const bubbleWrapperRef = useRef(null);
117
+ useLongPress(() => onContextMenuOpenChange === null || onContextMenuOpenChange === void 0 ? void 0 : onContextMenuOpenChange(true), enableLongPress ? bubbleWrapperRef : undefined, { delay: 500 });
105
118
  const showSenderInfo = useMemo(() => {
106
119
  var _a, _b, _c;
107
120
  try {
@@ -147,13 +160,13 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
147
160
  timeBreakNode,
148
161
  _jsx(SystemLogMessageItem, { message: message })));
149
162
  }
150
- const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage ? (_jsxs(_Fragment, { children: [_jsx(MergeImageMessageItem, { message: message }), (message === null || message === void 0 ? void 0 : message.textElem) && _jsx(TextMessageItem, { message: message })] })) : (renderMessageByType(message)) }));
151
- const wrappedBubble = hasContextMenu ? (_jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: ["contextMenu"], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, children: _jsx("div", { className: "min-w-0", children: bubbleNode }) })) : (bubbleNode);
163
+ const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: (message === null || message === void 0 ? void 0 : message.quoteElem) ? (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(QuoteMessageItem, { message: message, onPressQuoteMessage: onPressQuoteMessage }), renderMessageByType(message)] })) : (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage ? (_jsxs(_Fragment, { children: [_jsx(MergeImageMessageItem, { message: message }), (message === null || message === void 0 ? void 0 : message.textElem) && _jsx(TextMessageItem, { message: message })] })) : (renderMessageByType(message)) }));
164
+ const wrappedBubble = hasContextMenu ? (_jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: ["contextMenu"], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, overlayClassName: MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, children: _jsx("div", { ref: bubbleWrapperRef, className: "min-w-0", children: bubbleNode }) })) : (bubbleNode);
152
165
  return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
153
166
  timeBreakNode,
154
167
  _jsxs("div", { className: clsx("flex min-w-0 items-end gap-1", isMine ? "justify-end pl-12 pr-3" : "justify-start pl-3 pr-12"), children: [!isMine &&
155
168
  showPeerInfo &&
156
- (showAvatar ? (_jsx(MessageAvatar, { avatarUrl: message === null || message === void 0 ? void 0 : message.senderFaceUrl })) : (_jsx("div", { className: "h-6 w-6 shrink-0 self-end" }))), _jsxs("div", { className: clsx("flex min-w-0 max-w-[75%] flex-col gap-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 })), wrappedBubble] }), isMine && message.status === MessageStatus.Failed && (_jsx("span", { className: "mt-1 text-xs text-red-500", children: isMediaResendable(message) ? (_jsx(Trans, { i18nKey: "message_send_failed", components: {
169
+ (showAvatar ? (_jsx(MessageAvatar, { avatarUrl: message === null || message === void 0 ? void 0 : message.senderFaceUrl })) : (_jsx("div", { className: "h-6 w-6 shrink-0 self-end" }))), _jsxs("div", { className: clsx("flex min-w-0 max-w-[75%] flex-col gap-1", isMine ? "items-end" : "items-start"), children: [_jsxs("div", { className: "flex items-end gap-1.5", children: [isMine && (_jsx(MessageStatusIndicator, { message: message, isLatest: isLatestMine })), wrappedBubble] }), isMine && message.status === MessageStatus.Failed && (_jsx("span", { className: "mt-1 text-xs text-red-500", children: isMediaResendable(message) ? (_jsx(Trans, { i18nKey: "message_send_failed", components: {
157
170
  bold: (_jsx("strong", { className: "cursor-pointer", onClick: () => resendMessage(message) })),
158
171
  } })) : (t("message_send_failed_no_retry")) }))] })] })));
159
172
  };
@@ -0,0 +1,3 @@
1
+ export declare const ReplyPreviewBar: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default ReplyPreviewBar;
3
+ //# sourceMappingURL=ReplyPreviewBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplyPreviewBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/reply/ReplyPreviewBar.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,sDAuC3B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Icon } from "../../../components/icon";
4
+ import useAuthStore from "../../../store/auth";
5
+ import useConversationStore from "../../../store/conversation";
6
+ import { ReplyQuoteContent } from "./ReplyQuoteContent";
7
+ export const ReplyPreviewBar = () => {
8
+ const { t } = useTranslation();
9
+ const quotedMessage = useConversationStore((state) => state.quotedMessage);
10
+ const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
11
+ const userID = useAuthStore((state) => state.userID);
12
+ const handleClearQuotedMessage = () => {
13
+ setQuotedMessage(null);
14
+ };
15
+ if (!quotedMessage)
16
+ return null;
17
+ const isMe = quotedMessage.sendID === userID;
18
+ const senderName = isMe
19
+ ? t("your_self")
20
+ : quotedMessage.senderNickname || "";
21
+ return (_jsxs("div", { "data-testid": "reply-preview-bar", className: "flex h-[59px] w-full shrink-0 items-center bg-gray-200/10 px-2 py-1", children: [_jsx(ReplyQuoteContent, { message: quotedMessage, title: t("reply_to", { name: senderName }) }), _jsx("button", { type: "button", onClick: handleClearQuotedMessage, "aria-label": t("cancel"), "data-testid": "reply-preview-bar-close", className: "flex h-4 w-4 shrink-0 items-center justify-center", children: _jsx(Icon, { icon: "close-o", size: 16, className: "text-gray-600" }) })] }));
22
+ };
23
+ export default ReplyPreviewBar;
@@ -0,0 +1,7 @@
1
+ import { MessageItem } from "@openim/wasm-client-sdk";
2
+ interface ReplyPreviewMediaProps {
3
+ message: MessageItem;
4
+ }
5
+ declare const ReplyPreviewMedia: ({ message }: ReplyPreviewMediaProps) => import("react/jsx-runtime").JSX.Element | null;
6
+ export default ReplyPreviewMedia;
7
+ //# sourceMappingURL=ReplyPreviewMedia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplyPreviewMedia.d.ts","sourceRoot":"","sources":["../../../../src/components/message/reply/ReplyPreviewMedia.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAKnE,UAAU,sBAAsB;IAC9B,OAAO,EAAE,WAAW,CAAC;CACtB;AAYD,QAAA,MAAM,iBAAiB,GAAI,aAAa,sBAAsB,mDAoD7D,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MessageType } from "@openim/wasm-client-sdk";
3
+ import { documentIcon } from "../../../assets/svg";
4
+ import { Icon } from "../../../components/icon";
5
+ const parseUrlMetadataImage = (ex) => {
6
+ var _a;
7
+ if (!ex)
8
+ return undefined;
9
+ try {
10
+ const extendMessageInfo = JSON.parse(ex);
11
+ return (_a = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.urlMetadata) === null || _a === void 0 ? void 0 : _a.image;
12
+ }
13
+ catch (_b) {
14
+ return undefined;
15
+ }
16
+ };
17
+ const ReplyPreviewMedia = ({ message }) => {
18
+ var _a, _b, _c, _d, _e;
19
+ if (message.contentType === MessageType.FileMessage) {
20
+ return (_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden [&>svg]:h-6 [&>svg]:w-6", children: documentIcon }));
21
+ }
22
+ if (message.contentType === MessageType.PictureMessage) {
23
+ const imageUrl = ((_b = (_a = message.pictureElem) === null || _a === void 0 ? void 0 : _a.sourcePicture) === null || _b === void 0 ? void 0 : _b.url) ||
24
+ ((_d = (_c = message.pictureElem) === null || _c === void 0 ? void 0 : _c.snapshotPicture) === null || _d === void 0 ? void 0 : _d.url);
25
+ if (!imageUrl)
26
+ return null;
27
+ return (_jsx("img", { src: imageUrl, alt: "", className: "h-[42px] w-[42px] shrink-0 rounded-lg object-cover" }));
28
+ }
29
+ if (message.contentType === MessageType.VideoMessage) {
30
+ const posterUrl = (_e = message.videoElem) === null || _e === void 0 ? void 0 : _e.snapshotUrl;
31
+ return (_jsxs("div", { className: "relative h-[42px] w-[42px] shrink-0 overflow-hidden rounded-lg bg-gray-900", children: [posterUrl && (_jsx("img", { src: posterUrl, alt: "", className: "h-full w-full object-cover" })), _jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-black/60", children: _jsx(Icon, { icon: "play-o", size: 12, className: "text-white" }) }) })] }));
32
+ }
33
+ if (message.contentType === MessageType.UrlTextMessage) {
34
+ const imageUrl = parseUrlMetadataImage(message.ex);
35
+ if (!imageUrl)
36
+ return null;
37
+ return (_jsx("img", { src: imageUrl, alt: "", className: "h-[42px] w-[42px] shrink-0 rounded-lg object-cover" }));
38
+ }
39
+ return null;
40
+ };
41
+ export default ReplyPreviewMedia;
@@ -0,0 +1,9 @@
1
+ import { MessageItem, MessageType } from "@openim/wasm-client-sdk";
2
+ export declare const hasReplyMediaPreview: (contentType: MessageType) => boolean;
3
+ interface ReplyQuoteContentProps {
4
+ message: MessageItem;
5
+ title: string;
6
+ }
7
+ export declare const ReplyQuoteContent: ({ message, title }: ReplyQuoteContentProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default ReplyQuoteContent;
9
+ //# sourceMappingURL=ReplyQuoteContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplyQuoteContent.d.ts","sourceRoot":"","sources":["../../../../src/components/message/reply/ReplyQuoteContent.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AASnE,eAAO,MAAM,oBAAoB,GAAI,aAAa,WAAW,YACb,CAAC;AAEjD,UAAU,sBAAsB;IAC9B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,iBAAiB,GAAI,oBAAoB,sBAAsB,4CAsB3E,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import { MessageType } from "@openim/wasm-client-sdk";
4
+ import { generateContentBasedOnMessageType } from "../../../utils/common";
5
+ import ReplyPreviewMedia from "./ReplyPreviewMedia";
6
+ const TEXT_LIKE_MESSAGE_TYPES = [
7
+ MessageType.TextMessage,
8
+ MessageType.QuoteMessage,
9
+ ];
10
+ export const hasReplyMediaPreview = (contentType) => !TEXT_LIKE_MESSAGE_TYPES.includes(contentType);
11
+ export const ReplyQuoteContent = ({ message, title }) => {
12
+ var _a, _b;
13
+ const { t } = useTranslation();
14
+ const hasMedia = hasReplyMediaPreview(message.contentType);
15
+ return (_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-0.5 pr-2 self-stretch", children: [_jsx("div", { className: "w-[3px] shrink-0 self-stretch rounded bg-gray-cool-200" }), hasMedia && _jsx(ReplyPreviewMedia, { message: message }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center py-1 pl-2", children: [_jsx("p", { className: "truncate text-[14px] font-medium leading-[160%] text-gray-900", children: title }), _jsx("p", { className: "truncate text-[13px] leading-[160%] text-gray-400", children: generateContentBasedOnMessageType(message.contentType, ((_a = message.textElem) === null || _a === void 0 ? void 0 : _a.content) || ((_b = message.quoteElem) === null || _b === void 0 ? void 0 : _b.text), t) })] })] }));
16
+ };
17
+ export default ReplyQuoteContent;
@@ -0,0 +1,5 @@
1
+ import type { MenuProps } from "antd";
2
+ import { MessageToolboxAction } from "./types";
3
+ export declare const MESSAGE_TOOLBOX_OVERLAY_CLASSNAME: string;
4
+ export declare const buildMessageToolboxMenuItems: (actions: MessageToolboxAction[]) => NonNullable<MenuProps["items"]>;
5
+ //# sourceMappingURL=MessageToolboxMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageToolboxMenu.d.ts","sourceRoot":"","sources":["../../../../src/components/message/toolbox/MessageToolboxMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,iCAAiC,QAS7C,CAAC;AA0BF,eAAO,MAAM,4BAA4B,GACvC,SAAS,oBAAoB,EAAE,KAC9B,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAO5B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { Icon } from "../../../components/icon";
4
+ export const MESSAGE_TOOLBOX_OVERLAY_CLASSNAME = clsx("[&_.ant-dropdown-menu]:min-w-[200px]", "[&_.ant-dropdown-menu]:rounded-2xl", "[&_.ant-dropdown-menu]:p-0", "[&_.ant-dropdown-menu]:py-2", "[&_.ant-dropdown-menu]:shadow-message-toolbox", "[&_.ant-dropdown-menu-item]:rounded-none", "[&_.ant-dropdown-menu-item]:px-4", "[&_.ant-dropdown-menu-item]:py-1.5");
5
+ const ToolboxMenuRow = ({ icon, label, danger = false }) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Icon, { icon: icon, size: 24, className: danger ? "text-danger-400" : "text-gray-900" }), _jsx("span", { className: clsx("text-base font-medium", danger ? "text-danger-400" : "text-gray-900"), children: label })] }));
6
+ export const buildMessageToolboxMenuItems = (actions) => actions.map((action) => ({
7
+ key: action.key,
8
+ label: (_jsx(ToolboxMenuRow, { icon: action.icon, label: action.label, danger: action.danger })),
9
+ onClick: action.onClick,
10
+ }));
@@ -0,0 +1,4 @@
1
+ import { MessageItem as MessageItemType, MessageType } from "@openim/wasm-client-sdk";
2
+ export declare const isCopyableMessageType: (contentType?: MessageType) => boolean;
3
+ export declare const getCopyableMessageText: (message: MessageItemType) => string;
4
+ //# sourceMappingURL=getCopyableMessageText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCopyableMessageText.d.ts","sourceRoot":"","sources":["../../../../src/components/message/toolbox/getCopyableMessageText.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAUtF,eAAO,MAAM,qBAAqB,GAAI,cAAc,WAAW,KAAG,OACH,CAAC;AAEhE,eAAO,MAAM,sBAAsB,GAAI,SAAS,eAAe,KAAG,MAuBjE,CAAC"}