@droppii-org/chat-sdk 0.1.50 → 0.1.51

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 (88) hide show
  1. package/dist/assets/svg/index.d.ts +0 -1
  2. package/dist/assets/svg/index.d.ts.map +1 -1
  3. package/dist/assets/svg/index.js +0 -1
  4. package/dist/assets/svg/index.ts +1 -2
  5. package/dist/components/biz-inbox/BizThreadCard.d.ts.map +1 -1
  6. package/dist/components/biz-inbox/BizThreadCard.js +3 -11
  7. package/dist/components/biz-thread-detail/BizMessageList.js +2 -2
  8. package/dist/components/biz-thread-detail/BizThreadDetailInput.d.ts.map +1 -1
  9. package/dist/components/biz-thread-detail/BizThreadDetailInput.js +10 -19
  10. package/dist/components/biz-thread-detail/index.d.ts +0 -4
  11. package/dist/components/biz-thread-detail/index.d.ts.map +1 -1
  12. package/dist/components/biz-thread-detail/index.js +0 -2
  13. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  14. package/dist/components/conversation/ConversationBySessionItem.js +6 -5
  15. package/dist/components/conversation/ThreadCard.d.ts +14 -0
  16. package/dist/components/conversation/ThreadCard.d.ts.map +1 -0
  17. package/dist/components/conversation/ThreadCard.js +16 -0
  18. package/dist/components/media-collection/ImageCollection.d.ts.map +1 -1
  19. package/dist/components/media-collection/ImageCollection.js +3 -17
  20. package/dist/components/message/MessageList.d.ts.map +1 -1
  21. package/dist/components/message/MessageList.js +1 -7
  22. package/dist/components/message/footer/ComposerTextSyncPlugin.d.ts +6 -0
  23. package/dist/components/message/footer/ComposerTextSyncPlugin.d.ts.map +1 -0
  24. package/dist/components/message/footer/ComposerTextSyncPlugin.js +16 -0
  25. package/dist/components/message/footer/FilePreview.d.ts +3 -2
  26. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  27. package/dist/components/message/footer/FilePreview.js +61 -21
  28. package/dist/components/message/footer/LinkInputPreview.d.ts +7 -0
  29. package/dist/components/message/footer/LinkInputPreview.d.ts.map +1 -0
  30. package/dist/components/message/footer/LinkInputPreview.js +16 -0
  31. package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
  32. package/dist/components/message/footer/MediaActions.js +8 -5
  33. package/dist/components/message/footer/index.d.ts.map +1 -1
  34. package/dist/components/message/footer/index.js +48 -35
  35. package/dist/components/message/item/FileMessage.d.ts +2 -2
  36. package/dist/components/message/item/FileMessage.d.ts.map +1 -1
  37. package/dist/components/message/item/FileMessage.js +20 -12
  38. package/dist/components/message/item/ImageMessage.d.ts.map +1 -1
  39. package/dist/components/message/item/ImageMessage.js +11 -15
  40. package/dist/components/message/item/LinkCard.d.ts +7 -0
  41. package/dist/components/message/item/LinkCard.d.ts.map +1 -0
  42. package/dist/components/message/item/LinkCard.js +17 -0
  43. package/dist/components/message/item/LinkText.d.ts +6 -0
  44. package/dist/components/message/item/LinkText.d.ts.map +1 -0
  45. package/dist/components/message/item/LinkText.js +13 -0
  46. package/dist/components/message/item/MergeImageMessage.d.ts +7 -0
  47. package/dist/components/message/item/MergeImageMessage.d.ts.map +1 -0
  48. package/dist/components/message/item/MergeImageMessage.js +30 -0
  49. package/dist/components/message/item/MessageBubble.d.ts +13 -0
  50. package/dist/components/message/item/MessageBubble.d.ts.map +1 -0
  51. package/dist/components/message/item/MessageBubble.js +19 -0
  52. package/dist/components/message/item/RevokeMessage.js +1 -1
  53. package/dist/components/message/item/TextMessage.d.ts.map +1 -1
  54. package/dist/components/message/item/TextMessage.js +20 -9
  55. package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
  56. package/dist/components/message/item/UrlTextMessage.js +19 -17
  57. package/dist/components/message/item/VideoMessage.d.ts.map +1 -1
  58. package/dist/components/message/item/VideoMessage.js +4 -12
  59. package/dist/components/message/item/imagePreviewToolbar.d.ts +5 -0
  60. package/dist/components/message/item/imagePreviewToolbar.d.ts.map +1 -0
  61. package/dist/components/message/item/imagePreviewToolbar.js +14 -0
  62. package/dist/components/message/item/index.d.ts +2 -1
  63. package/dist/components/message/item/index.d.ts.map +1 -1
  64. package/dist/components/message/item/index.js +38 -41
  65. package/dist/components/message/item/textStyle.d.ts +2 -0
  66. package/dist/components/message/item/textStyle.d.ts.map +1 -0
  67. package/dist/components/message/item/textStyle.js +1 -0
  68. package/dist/hooks/message/useSendMessage.d.ts +3 -10
  69. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  70. package/dist/hooks/message/useSendMessage.js +27 -44
  71. package/dist/styles/global.css +1 -1
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/dist/utils/bizMessage.d.ts +1 -14
  74. package/dist/utils/bizMessage.d.ts.map +1 -1
  75. package/dist/utils/bizMessage.js +0 -26
  76. package/dist/utils/common.d.ts +2 -1
  77. package/dist/utils/common.d.ts.map +1 -1
  78. package/dist/utils/common.js +9 -3
  79. package/dist/utils/fileValidation.d.ts +1 -0
  80. package/dist/utils/fileValidation.d.ts.map +1 -1
  81. package/dist/utils/fileValidation.js +10 -17
  82. package/dist/utils/link.d.ts +13 -0
  83. package/dist/utils/link.d.ts.map +1 -0
  84. package/dist/utils/link.js +77 -0
  85. package/dist/utils/openLink.d.ts +3 -0
  86. package/dist/utils/openLink.d.ts.map +1 -0
  87. package/dist/utils/openLink.js +19 -0
  88. package/package.json +126 -127
@@ -8,7 +8,7 @@ import clsx from "clsx";
8
8
  import { useTranslation } from "react-i18next";
9
9
  import EmojiPicker from "./EmojiPicker";
10
10
  import { Icon } from "../../../components/icon";
11
- import { validateFile, validateVideoLimit, } from "../../../utils/fileValidation";
11
+ import { createUploadFile, validateFile, validateVideoLimit, } from "../../../utils/fileValidation";
12
12
  import { DOCUMENT_TYPES } from "./editorConfig";
13
13
  import CannedResponseIcon from "../../../assets/svg/canned-response";
14
14
  import { useMessageFooterContext } from ".";
@@ -58,15 +58,18 @@ export default function MediaActions({ listUploadFiles, onFilesChange, buttonCla
58
58
  return false;
59
59
  };
60
60
  const handleChange = (info) => {
61
- var _a;
62
61
  let newList = [...info.fileList];
63
62
  const lastFile = info.file;
64
63
  if (DOCUMENT_TYPES.includes(lastFile.type || "")) {
65
64
  newList = newList.filter((f) => !DOCUMENT_TYPES.includes(f.type || ""));
66
- const originFile = ((_a = lastFile.originFileObj) !== null && _a !== void 0 ? _a : lastFile);
67
- newList.push(Object.assign(Object.assign({}, lastFile), { originFileObj: originFile }));
65
+ newList.push(lastFile);
68
66
  }
69
- onFilesChange(newList);
67
+ const normalizedList = newList.map((file) => {
68
+ var _a;
69
+ const originFile = ((_a = file.originFileObj) !== null && _a !== void 0 ? _a : file);
70
+ return Object.assign(Object.assign({}, createUploadFile(originFile)), { uid: file.uid });
71
+ });
72
+ onFilesChange(normalizedList);
70
73
  };
71
74
  const onPressCanned = useCallback(async () => {
72
75
  if (!isOpenCanned) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAe,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAKrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAkB/C,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAY/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CA8QpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAEL,wBAAwB,EAEzB,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAkB/C,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAY/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CAuSpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -5,13 +5,17 @@ import { ToolbarPlugin } from "./ToolbarPlugin";
5
5
  import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
6
6
  import { ListPlugin } from "@lexical/react/LexicalListPlugin";
7
7
  import { editorTheme, editorNodes, editorOnError } from "./editorConfig";
8
- import { createContext, useCallback, useContext, useEffect, useRef, useState, } from "react";
8
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
9
9
  import EnterHandler from "./EnterHandler";
10
10
  import ActionBar from "./ActionBar";
11
- import { AccountType } from "../../../types/chat";
11
+ import { AccountType, } from "../../../types/chat";
12
12
  import { useSendMessage } from "../../../hooks/message/useSendMessage";
13
13
  import { Modal, Popover } from "antd";
14
14
  import FilePreview from "./FilePreview";
15
+ import LinkInputPreview from "./LinkInputPreview";
16
+ import ComposerTextSyncPlugin from "./ComposerTextSyncPlugin";
17
+ import { extractLinkUrls } from "../../../utils/link";
18
+ import { useFetchUrlMetadata } from "../../../hooks/common/useFetchUrlMetadata";
15
19
  import { useTranslation } from "react-i18next";
16
20
  import PasteAndDropPlugin from "./PasteAndDropPlugin";
17
21
  import { QuotedMessageFooter } from "./QuotedMessage";
@@ -48,9 +52,11 @@ export const MessageFooterContext = createContext({
48
52
  export const useMessageFooterContext = () => useContext(MessageFooterContext);
49
53
  const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
50
54
  const { t } = useTranslation();
51
- const { sendTextMessage, sendMergeMessage } = useSendMessage();
55
+ const { sendMergeMessage } = useSendMessage();
52
56
  const accountType = useCurrentUserAccountType();
53
57
  const [listUploadFiles, setListUploadFiles] = useState([]);
58
+ const [composerText, setComposerText] = useState("");
59
+ const [isLinkPreviewDismissed, setIsLinkPreviewDismissed] = useState(false);
54
60
  const [isOpenCanned, { setTrue: openCanned, setFalse: closeCanned }] = useBoolean(false);
55
61
  const isCrm = useAuthStore((state) => state.isCrm);
56
62
  const [cannedQuery, setCannedQuery] = useState("");
@@ -61,6 +67,25 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
61
67
  const quotedMessage = useConversationStore((state) => state.quotedMessage);
62
68
  const conversationData = useConversationStore((state) => state.conversationData);
63
69
  const lockToInternal = Boolean((quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID) && (quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.isInternal));
70
+ const previewUrl = useMemo(() => {
71
+ const urls = extractLinkUrls(composerText);
72
+ return urls.length === 1 ? urls[0] : null;
73
+ }, [composerText]);
74
+ const showLinkPreview = !!previewUrl && !isLinkPreviewDismissed && !isInternal;
75
+ const { data: previewMetadata } = useFetchUrlMetadata(previewUrl !== null && previewUrl !== void 0 ? previewUrl : "", showLinkPreview);
76
+ useEffect(() => {
77
+ setIsLinkPreviewDismissed(false);
78
+ }, [previewUrl]);
79
+ const resolveUrlMetadata = useCallback(() => {
80
+ if (!showLinkPreview || !previewUrl || !previewMetadata)
81
+ return undefined;
82
+ return {
83
+ url: previewUrl,
84
+ title: previewMetadata.title,
85
+ description: previewMetadata.description,
86
+ image: previewMetadata.image,
87
+ };
88
+ }, [showLinkPreview, previewUrl, previewMetadata]);
64
89
  const [isSessionClosedModalOpen, { setTrue: openSessionClosedModal, setFalse: closeSessionClosedModal },] = useBoolean(false);
65
90
  const pendingMessageRef = useRef(null);
66
91
  const { mutateAsync: createSession, isPending: isCreatingSession } = useCreateSession();
@@ -90,57 +115,45 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
90
115
  type,
91
116
  isInternal,
92
117
  files: [...listUploadFiles],
118
+ urlMetadata: resolveUrlMetadata(),
93
119
  };
94
120
  openSessionClosedModal();
95
121
  return false;
96
122
  }
97
- if (type === "text") {
98
- sendTextMessage({ plainText, richText, currentSession, isInternal });
99
- }
100
- else {
101
- sendMergeMessage({
102
- plainText,
103
- richText,
104
- files: listUploadFiles,
105
- currentSession,
106
- isInternal,
107
- });
108
- }
123
+ sendMergeMessage({
124
+ plainText,
125
+ richText,
126
+ files: listUploadFiles,
127
+ currentSession,
128
+ isInternal,
129
+ urlMetadata: resolveUrlMetadata(),
130
+ });
109
131
  setListUploadFiles([]);
110
132
  return true;
111
133
  }, [
112
134
  sendMergeMessage,
113
- sendTextMessage,
114
135
  listUploadFiles,
115
136
  currentSession,
116
137
  openSessionClosedModal,
138
+ resolveUrlMetadata,
117
139
  ]);
118
140
  const executePendingSend = useCallback((newSession) => {
119
141
  const pending = pendingMessageRef.current;
120
142
  if (!pending)
121
143
  return;
122
144
  const sessionToUse = newSession || currentSession;
123
- if (pending.type === "text") {
124
- sendTextMessage({
125
- plainText: pending.plainText,
126
- richText: pending.richText,
127
- currentSession: sessionToUse,
128
- isInternal: pending.isInternal,
129
- });
130
- }
131
- else {
132
- sendMergeMessage({
133
- plainText: pending.plainText,
134
- richText: pending.richText,
135
- files: pending.files,
136
- currentSession: sessionToUse,
137
- isInternal: pending.isInternal,
138
- });
139
- }
145
+ sendMergeMessage({
146
+ plainText: pending.plainText,
147
+ richText: pending.richText,
148
+ files: pending.files,
149
+ currentSession: sessionToUse,
150
+ isInternal: pending.isInternal,
151
+ urlMetadata: pending.urlMetadata,
152
+ });
140
153
  setListUploadFiles([]);
141
154
  pendingMessageRef.current = null;
142
155
  emit("CLEAR_COMPOSER");
143
- }, [sendTextMessage, sendMergeMessage, currentSession]);
156
+ }, [sendMergeMessage, currentSession]);
144
157
  const handleSessionAction = useCallback(async (action) => {
145
158
  const conversationId = conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID;
146
159
  if (!conversationId)
@@ -202,6 +215,6 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
202
215
  setMaxActiveCannedIndex,
203
216
  }, 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: {
204
217
  body: "mx-1 !p-0",
205
- }, 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, {}), _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(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }), _jsx(SessionClosedModal, { open: isSessionClosedModalOpen, loading: isSessionActionLoading, onReopen: handleReopenSession, onCreateNew: handleCreateNewSession, onClose: handleCloseSessionModal })] }));
218
+ }, 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 })] }));
206
219
  };
207
220
  export default MessageFooterProvider;
@@ -1,7 +1,7 @@
1
1
  import { MessageItem } from "@openim/wasm-client-sdk";
2
- interface ImageMessageItemProps {
2
+ interface FileMessageItemProps {
3
3
  message: MessageItem;
4
4
  }
5
- declare const FileMessageItem: (props: ImageMessageItemProps) => import("react/jsx-runtime").JSX.Element;
5
+ declare const FileMessageItem: (props: FileMessageItemProps) => import("react/jsx-runtime").JSX.Element;
6
6
  export default FileMessageItem;
7
7
  //# sourceMappingURL=FileMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/FileMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAItD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,GAAI,OAAO,qBAAqB,4CAmCpD,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"FileMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/FileMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAOrE,UAAU,oBAAoB;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,GAAI,OAAO,oBAAoB,4CAiEnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,28 +1,36 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { shortenFileName } from "../../../components/message/footer/FilePreview";
2
+ import { MessageStatus } from "@openim/wasm-client-sdk";
3
+ import { Spin } from "antd";
4
+ import { Icon } from "../../../components/icon";
3
5
  import { documentIcon } from "../../../assets/svg";
6
+ import { shortenFileName } from "../../../components/message/footer/FilePreview";
7
+ import { renderFileSize } from "../../../utils/common";
4
8
  const FileMessageItem = (props) => {
5
9
  var _a;
6
10
  const { message } = props;
11
+ const fileElem = message.fileElem;
12
+ const fileName = (fileElem === null || fileElem === void 0 ? void 0 : fileElem.fileName) || "";
13
+ const fileSize = (_a = fileElem === null || fileElem === void 0 ? void 0 : fileElem.fileSize) !== null && _a !== void 0 ? _a : 0;
14
+ const sourceUrl = (fileElem === null || fileElem === void 0 ? void 0 : fileElem.sourceUrl) || "";
15
+ const isSending = message.status === MessageStatus.Sending;
16
+ const hasUrl = !!sourceUrl;
17
+ const handleView = () => {
18
+ if (!hasUrl)
19
+ return;
20
+ window.open(sourceUrl, "_blank", "noopener,noreferrer");
21
+ };
7
22
  const handleDownload = () => {
8
- var _a, _b;
9
- const url = ((_a = message.fileElem) === null || _a === void 0 ? void 0 : _a.sourceUrl) || "";
10
- const fileName = ((_b = message.fileElem) === null || _b === void 0 ? void 0 : _b.fileName) || "";
11
- if (!url) {
12
- console.warn("Không có link file để tải");
23
+ if (!hasUrl)
13
24
  return;
14
- }
15
25
  const link = document.createElement("a");
16
- link.href = url;
17
- if (fileName) {
18
- link.setAttribute("download", fileName);
19
- }
26
+ link.href = sourceUrl;
27
+ link.setAttribute("download", fileName);
20
28
  link.setAttribute("target", "_blank");
21
29
  link.setAttribute("rel", "noopener noreferrer");
22
30
  document.body.appendChild(link);
23
31
  link.click();
24
32
  link.remove();
25
33
  };
26
- return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2 cursor-pointer", onClick: handleDownload, children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(((_a = message.fileElem) === null || _a === void 0 ? void 0 : _a.fileName) || "") })] }));
34
+ return (_jsx(Spin, { spinning: isSending, children: _jsxs("div", { className: "flex h-[51px] w-[292px] items-center gap-3 rounded-xl border border-[rgba(168,173,175,0.2)] bg-white px-3 py-1", children: [_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden [&>svg]:h-6 [&>svg]:w-6", children: documentIcon }), _jsxs("div", { className: "flex min-w-0 w-[168px] flex-col justify-center", children: [_jsx("span", { className: "truncate text-[14px] font-medium leading-[160%] text-[#393E40]", children: shortenFileName(fileName) }), _jsx("span", { className: "truncate text-[13px] leading-[160%] text-[#5C6366]", children: renderFileSize(fileSize) })] }), _jsxs("div", { className: "flex w-[52px] shrink-0 items-center justify-end gap-3", children: [_jsx("button", { type: "button", onClick: handleView, disabled: !hasUrl, "aria-label": "Xem file", className: !hasUrl ? "opacity-30" : undefined, children: _jsx(Icon, { icon: "eye-o", size: 20, className: "text-[#393E40]" }) }), _jsx("button", { type: "button", onClick: handleDownload, disabled: !hasUrl, "aria-label": "T\u1EA3i xu\u1ED1ng", className: !hasUrl ? "opacity-30" : undefined, children: _jsx(Icon, { icon: "download-o", size: 20, className: "text-[#393E40]" }) })] })] }) }));
27
35
  };
28
36
  export default FileMessageItem;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/ImageMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAGrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAoCrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ImageMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/ImageMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAKrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CA+BrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,23 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { MessageStatus } from "@openim/wasm-client-sdk";
3
3
  import { Image, Spin } from "antd";
4
- const min = (a, b) => (a > b ? b : a);
4
+ import { useTranslation } from "react-i18next";
5
+ import { renderImagePreviewToolbar } from "./imagePreviewToolbar";
5
6
  const ImageMessageItem = (props) => {
6
- var _a, _b, _c, _d;
7
+ var _a, _b, _c;
7
8
  const { message } = props;
8
- const imageHeight = message.pictureElem.sourcePicture.height;
9
- const imageWidth = message.pictureElem.sourcePicture.width;
10
- const snapshotMaxHeight = (_b = (_a = message.pictureElem.snapshotPicture) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : imageHeight;
11
- const minHeight = min(200, imageWidth) * (imageHeight / imageWidth) + 2;
12
- const adaptedHight = min(minHeight, snapshotMaxHeight) + 10;
13
- const adaptedWidth = min(imageWidth, 200) + 10;
14
- const sourceUrl = ((_c = message.pictureElem.sourcePicture) === null || _c === void 0 ? void 0 : _c.url) ||
15
- ((_d = message.pictureElem.snapshotPicture) === null || _d === void 0 ? void 0 : _d.url);
9
+ const { t } = useTranslation();
10
+ const pictureElem = message.pictureElem;
11
+ const sourceUrl = ((_a = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.sourcePicture) === null || _a === void 0 ? void 0 : _a.url) || ((_b = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.snapshotPicture) === null || _b === void 0 ? void 0 : _b.url) || "";
12
+ const bigUrl = ((_c = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.bigPicture) === null || _c === void 0 ? void 0 : _c.url) || sourceUrl;
16
13
  const isSending = message.status === MessageStatus.Sending;
17
- const minStyle = {
18
- minHeight: `${adaptedHight}px`,
19
- minWidth: `${adaptedWidth}px`,
20
- };
21
- return (_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "relative max-w-[200px]", style: minStyle, children: _jsx(Image, { rootClassName: "message-image cursor-pointer", className: "rounded-md", src: sourceUrl, preview: true, placeholder: _jsx("div", { style: minStyle, className: "flex items-center justify-center", children: _jsx(Spin, {}) }) }) }) }));
14
+ return (_jsx("div", { className: "-mx-1", children: _jsx(Spin, { spinning: isSending, children: _jsx(Image, { src: sourceUrl, width: 205, height: 205, className: "cursor-pointer rounded-xl object-cover", preview: {
15
+ src: bigUrl,
16
+ toolbarRender: (originalNode, info) => { var _a; return renderImagePreviewToolbar(originalNode, (_a = info === null || info === void 0 ? void 0 : info.image) === null || _a === void 0 ? void 0 : _a.url, t); },
17
+ }, placeholder: _jsx("div", { className: "flex h-[205px] w-[205px] items-center justify-center rounded-xl bg-black/10", children: _jsx(Spin, {}) }) }) }) }));
22
18
  };
23
19
  export default ImageMessageItem;
@@ -0,0 +1,7 @@
1
+ import type { UrlMetadataInfo } from "../../../types/chat";
2
+ export interface LinkCardProps {
3
+ metadata: UrlMetadataInfo;
4
+ }
5
+ declare const LinkCard: ({ metadata }: LinkCardProps) => import("react/jsx-runtime").JSX.Element | null;
6
+ export default LinkCard;
7
+ //# sourceMappingURL=LinkCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkCard.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/LinkCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,QAAA,MAAM,QAAQ,GAAI,cAAc,aAAa,mDA0C5C,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from "react-i18next";
4
+ import { getHostLabel } from "../../../utils/link";
5
+ import { openLink } from "../../../utils/openLink";
6
+ const LinkCard = ({ metadata }) => {
7
+ const { t } = useTranslation("biz-inbox");
8
+ const { url, title, description, image } = metadata;
9
+ if (!title && !description && !image) {
10
+ return null;
11
+ }
12
+ const handleOpen = () => {
13
+ openLink(url, t);
14
+ };
15
+ return (_jsxs("button", { type: "button", onClick: handleOpen, "data-testid": "link-message-card", className: "flex w-full max-w-[292px] flex-col overflow-hidden rounded-[12px] border border-[rgba(57,62,64,0.1)] bg-white text-left", children: [image && (_jsx("div", { className: "aspect-[16/9] w-full overflow-hidden", children: _jsx("img", { src: image, alt: "", className: "h-full w-full object-cover" }) })), _jsxs("div", { className: "flex flex-col px-3 py-2", children: [title && (_jsx("p", { className: "truncate text-[14px] font-medium leading-[1.6] text-[#393E40]", children: title })), _jsx("p", { className: "truncate text-[12px] font-medium leading-[1.6] tracking-[0.12px] text-[#1B3FE4]", children: getHostLabel(url) }), description && (_jsx("p", { className: "line-clamp-2 text-[12px] leading-[1.6] text-[#5C6366]", children: description }))] })] }));
16
+ };
17
+ export default LinkCard;
@@ -0,0 +1,6 @@
1
+ export interface LinkTextProps {
2
+ text: string;
3
+ }
4
+ declare const LinkText: ({ text }: LinkTextProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default LinkText;
6
+ //# sourceMappingURL=LinkText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkText.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/LinkText.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,QAAQ,GAAI,UAAU,aAAa,4CA2BxC,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { useTranslation } from "react-i18next";
5
+ import { parseTextWithLinks } from "../../../utils/link";
6
+ import { openLink } from "../../../utils/openLink";
7
+ import { MESSAGE_TEXT_CLASS } from "./textStyle";
8
+ const LinkText = ({ text }) => {
9
+ const { t } = useTranslation("biz-inbox");
10
+ const segments = parseTextWithLinks(text);
11
+ return (_jsx("span", { className: clsx("min-w-0 whitespace-pre-wrap text-left [overflow-wrap:anywhere]", MESSAGE_TEXT_CLASS), children: segments.map((segment, index) => segment.type === "link" ? (_jsx("button", { type: "button", onClick: () => openLink(segment.url, t), className: "inline text-left font-medium text-[#1B3FE4] underline-offset-2 hover:underline", children: segment.value }, `${segment.url}-${index}`)) : (_jsx("span", { children: segment.value }, `text-${index}`))) }));
12
+ };
13
+ export default LinkText;
@@ -0,0 +1,7 @@
1
+ import { MessageItem } from "@openim/wasm-client-sdk";
2
+ interface MergeImageMessageItemProps {
3
+ message: MessageItem;
4
+ }
5
+ declare const MergeImageMessageItem: (props: MergeImageMessageItemProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default MergeImageMessageItem;
7
+ //# sourceMappingURL=MergeImageMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MergeImageMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MergeImageMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAMrE,UAAU,0BAA0B;IAClC,OAAO,EAAE,WAAW,CAAC;CACtB;AAQD,QAAA,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,4CA4C/D,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MessageStatus } from "@openim/wasm-client-sdk";
3
+ import { Image, Spin } from "antd";
4
+ import clsx from "clsx";
5
+ import { useTranslation } from "react-i18next";
6
+ import { renderImagePreviewToolbar } from "./imagePreviewToolbar";
7
+ const MERGE_IMAGE_GRID_COLS_CLASS = {
8
+ 1: "grid-cols-[repeat(1,100px)]",
9
+ 2: "grid-cols-[repeat(2,100px)]",
10
+ 3: "grid-cols-[repeat(3,100px)]",
11
+ };
12
+ const MergeImageMessageItem = (props) => {
13
+ var _a, _b;
14
+ const { message } = props;
15
+ const { t } = useTranslation();
16
+ const subMessages = (_b = (_a = message.mergeElem) === null || _a === void 0 ? void 0 : _a.multiMessage) !== null && _b !== void 0 ? _b : [];
17
+ const isSending = message.status === MessageStatus.Sending;
18
+ const cols = Math.min(subMessages.length, 3);
19
+ return (_jsx("div", { className: "-mx-1", children: _jsx(Spin, { spinning: isSending, children: _jsx(Image.PreviewGroup, { preview: {
20
+ toolbarRender: (originalNode, info) => { var _a; return renderImagePreviewToolbar(originalNode, (_a = info === null || info === void 0 ? void 0 : info.image) === null || _a === void 0 ? void 0 : _a.url, t); },
21
+ }, children: _jsx("div", { className: clsx("grid gap-1", MERGE_IMAGE_GRID_COLS_CLASS[cols]), children: subMessages.map((item) => {
22
+ var _a, _b, _c, _d, _e, _f;
23
+ const sourceUrl = ((_b = (_a = item.pictureElem) === null || _a === void 0 ? void 0 : _a.sourcePicture) === null || _b === void 0 ? void 0 : _b.url) ||
24
+ ((_d = (_c = item.pictureElem) === null || _c === void 0 ? void 0 : _c.snapshotPicture) === null || _d === void 0 ? void 0 : _d.url) ||
25
+ "";
26
+ const bigUrl = ((_f = (_e = item.pictureElem) === null || _e === void 0 ? void 0 : _e.bigPicture) === null || _f === void 0 ? void 0 : _f.url) || sourceUrl;
27
+ return (_jsx(Image, { src: sourceUrl, width: 100, height: 100, className: "cursor-pointer rounded-xl object-cover", preview: { src: bigUrl }, placeholder: _jsx("div", { className: "flex h-[100px] w-[100px] items-center justify-center rounded-xl bg-black/10", children: _jsx(Spin, {}) }) }, item.clientMsgID));
28
+ }) }) }) }) }));
29
+ };
30
+ export default MergeImageMessageItem;
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from "react";
2
+ export interface MessageBubbleProps {
3
+ isMine: boolean;
4
+ content: ReactNode;
5
+ senderName?: string;
6
+ showSenderName?: boolean;
7
+ showTip?: boolean;
8
+ isInternal?: boolean;
9
+ id?: string;
10
+ }
11
+ declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
12
+ export default MessageBubble;
13
+ //# sourceMappingURL=MessageBubble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAmCD,QAAA,MAAM,aAAa,GAAI,2EAQpB,kBAAkB,4CAsCpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ const INCOMING_TAIL_PATH = "M1.3798 18H4V0C3.51762 4.5 3.34269 10.5 0.966518 14C-0.568384 16.2608 -0.159364 18 1.3798 18Z";
5
+ const OUTGOING_TAIL_PATH = "M2.6202 18H0V0C0.482375 4.5 0.657308 10.5 3.03348 14C4.56838 16.2608 4.15936 18 2.6202 18Z";
6
+ const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className: "flex shrink-0 items-center justify-center", children: _jsx("div", { className: "flex-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "4", height: "18", viewBox: "0 0 4 18", fill: "none", "aria-hidden": true, children: _jsx("path", { d: isMine ? OUTGOING_TAIL_PATH : INCOMING_TAIL_PATH, fill: isInternal ? "#FF9500" : isMine ? "#0051FF" : "#83899D", fillOpacity: "0.1" }) }) }) }));
7
+ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, }) => {
8
+ const bubbleClass = clsx("relative max-w-full p-2", showTip
9
+ ? isMine
10
+ ? "rounded-[20px] rounded-br-none"
11
+ : "rounded-[20px] rounded-bl-none"
12
+ : "rounded-[20px]", isInternal
13
+ ? "bg-[rgba(255,149,0,0.1)]"
14
+ : isMine
15
+ ? "bg-[rgba(0,81,255,0.1)]"
16
+ : "bg-[rgba(131,137,157,0.1)]");
17
+ return (_jsxs("div", { className: "relative flex items-end", "data-testid": "message-bubble", children: [!isMine && showTip && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), _jsx("div", { className: bubbleClass, id: id, children: _jsxs("div", { className: "flex flex-col gap-1 px-1", children: [showSenderName && senderName && (_jsx("span", { className: "text-[12px] font-medium leading-[160%] tracking-[0.12px] text-[#747B7E]", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTip && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal }))] }));
18
+ };
19
+ export default MessageBubble;
@@ -3,6 +3,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  const RevokeMessageItem = (_) => {
5
5
  const { t } = useTranslation();
6
- return (_jsx("span", { className: "!text-sm sm:text-base whitespace-pre-wrap italic text-gray-500", children: t("revoked") }));
6
+ return (_jsx("span", { className: "whitespace-pre-wrap text-[16px] leading-[160%] italic text-gray-500", children: t("revoked") }));
7
7
  };
8
8
  export default RevokeMessageItem;
@@ -1 +1 @@
1
- {"version":3,"file":"TextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/TextMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAKnE,UAAU,oBAAoB;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,QAAA,MAAM,eAAe,GAAI,OAAO,oBAAoB,mDAoCnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"TextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/TextMessage.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAQnE,UAAU,oBAAoB;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,GAAI,OAAO,oBAAoB,mDAiDnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,10 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import clsx from "clsx";
3
4
  import { MessageType } from "@openim/wasm-client-sdk";
4
5
  import useAuthStore from "../../../store/auth";
5
- import { sanitizeHtml } from "../../../utils/common";
6
+ import { htmlHasFormatting, sanitizeHtml, stripHtml } from "../../../utils/common";
7
+ import { extractLinkUrls } from "../../../utils/link";
8
+ import LinkText from "./LinkText";
9
+ import { MESSAGE_TEXT_CLASS } from "./textStyle";
6
10
  const TextMessageItem = (props) => {
7
- var _a, _b, _c;
11
+ var _a, _b, _c, _d;
8
12
  const { message } = props;
9
13
  const isCrm = useAuthStore((state) => state.isCrm);
10
14
  let extendMessageInfo = null;
@@ -18,13 +22,20 @@ const TextMessageItem = (props) => {
18
22
  if ((message === null || message === void 0 ? void 0 : message.contentType) === MessageType.CustomMessage && !isCrm) {
19
23
  return null;
20
24
  }
21
- if (Object.keys(extendMessageInfo).length > 0 &&
22
- (extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo)) {
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
- // 🔒 Sanitize HTML to prevent XSS attacks
25
- const sanitizedContent = sanitizeHtml(htmlContent);
26
- return (_jsx("div", { className: "!text-sm sm:text-base break-normal whitespace-pre-line flex-1", style: { overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }));
25
+ const richHtmlContent = ((_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) || "";
26
+ if (richHtmlContent && htmlHasFormatting(richHtmlContent)) {
27
+ const sanitizedContent = sanitizeHtml(richHtmlContent);
28
+ return (_jsx("div", { className: clsx("break-normal whitespace-pre-line flex-1", MESSAGE_TEXT_CLASS), style: { overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }));
27
29
  }
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) || "" }));
30
+ const text = ((_c = message === null || message === void 0 ? void 0 : message.textElem) === null || _c === void 0 ? void 0 : _c.content) ||
31
+ ((_d = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _d === void 0 ? void 0 : _d.text) ||
32
+ stripHtml(richHtmlContent) ||
33
+ "";
34
+ if (!text)
35
+ return null;
36
+ if (extractLinkUrls(text).length > 0) {
37
+ return _jsx(LinkText, { text: text });
38
+ }
39
+ return (_jsx("p", { className: clsx("whitespace-pre-wrap break-words", MESSAGE_TEXT_CLASS), children: text }));
29
40
  };
30
41
  export default TextMessageItem;
@@ -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;AAUtD,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;AAatD,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,kBAAkB,GAAI,OAAO,uBAAuB,4CA6DzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1,18 +1,14 @@
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";
4
- import { useFetchExternalLink } from "../../../hooks/common/useFetchExternalLink";
5
- import { Image } from "antd";
3
+ import { wrapLinksInHtml, sanitizeHtml, htmlHasFormatting, stripHtml, } from "../../../utils/common";
4
+ import clsx from "clsx";
5
+ import LinkText from "./LinkText";
6
+ import LinkCard from "./LinkCard";
7
+ import { MESSAGE_TEXT_CLASS } from "./textStyle";
6
8
  const UrlTextMessageItem = (props) => {
7
- var _a, _b, _c, _d, _e, _f, _g, _h;
9
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
8
10
  const { message } = props;
9
- const { data: externalLinkData } = useFetchExternalLink(((_b = (_a = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _a === void 0 ? void 0 : _a.urls) === null || _b === void 0 ? void 0 : _b[0]) || "");
10
11
  let extendMessageInfo = null;
11
- const onPressExternalLink = (url) => {
12
- if (!url)
13
- return;
14
- window.open(url, "_blank", "noopener,noreferrer");
15
- };
16
12
  try {
17
13
  extendMessageInfo = JSON.parse((message === null || message === void 0 ? void 0 : message.ex) || "{}");
18
14
  }
@@ -20,13 +16,19 @@ const UrlTextMessageItem = (props) => {
20
16
  extendMessageInfo = {};
21
17
  console.error("Failed to parse extendMessageInfo", error);
22
18
  }
23
- if (Object.keys(extendMessageInfo).length > 0 &&
24
- (extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo)) {
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
- // 🔒 Sanitize HTML to prevent XSS attacks
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-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 }))] }))] }));
19
+ const richHtmlContent = ((_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) || "";
20
+ if (richHtmlContent && htmlHasFormatting(richHtmlContent)) {
21
+ const sanitizedContent = sanitizeHtml(wrapLinksInHtml(richHtmlContent));
22
+ return (_jsx("div", { className: clsx("break-normal whitespace-pre-line flex-1", MESSAGE_TEXT_CLASS), style: { overflowWrap: "anywhere" }, dangerouslySetInnerHTML: { __html: sanitizedContent } }));
29
23
  }
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]) || "" }));
24
+ const text = (_h = (_g = (_f = (_d = (_c = message === null || message === void 0 ? void 0 : message.textElem) === null || _c === void 0 ? void 0 : _c.content) !== null && _d !== void 0 ? _d : (_e = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _e === void 0 ? void 0 : _e.content) !== null && _f !== void 0 ? _f : message === null || message === void 0 ? void 0 : message.content) !== null && _g !== void 0 ? _g : stripHtml(richHtmlContent)) !== null && _h !== void 0 ? _h : "";
25
+ const urlMetadata = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.urlMetadata;
26
+ if (urlMetadata === null || urlMetadata === void 0 ? void 0 : urlMetadata.url) {
27
+ return (_jsxs("div", { className: "flex max-w-[292px] flex-col gap-2", children: [text && _jsx(LinkText, { text: text }), _jsx(LinkCard, { metadata: urlMetadata })] }));
28
+ }
29
+ if (text) {
30
+ return _jsx(LinkText, { text: text });
31
+ }
32
+ return (_jsx("span", { className: clsx("break-normal whitespace-pre-line flex-1", MESSAGE_TEXT_CLASS), style: { overflowWrap: "anywhere" }, children: ((_k = (_j = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _j === void 0 ? void 0 : _j.urls) === null || _k === void 0 ? void 0 : _k[0]) || "" }));
31
33
  };
32
34
  export default UrlTextMessageItem;
@@ -1 +1 @@
1
- {"version":3,"file":"VideoMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/VideoMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAGrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAsBrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"VideoMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/VideoMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAGrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAmBrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,20 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { MessageStatus } from "@openim/wasm-client-sdk";
3
3
  import { Spin } from "antd";
4
- const min = (a, b) => (a > b ? b : a);
5
4
  const VideoMessageItem = (props) => {
6
5
  const { message } = props;
7
- const imageHeight = message.videoElem.snapshotHeight;
8
- const imageWidth = message.videoElem.snapshotWidth;
9
- const minHeight = min(200, imageWidth) * (imageHeight / imageWidth) + 2;
10
- const adaptedHight = min(minHeight, imageHeight) + 10;
11
- const adaptedWidth = min(imageWidth, 200) + 10;
12
- const sourceUrl = message.videoElem.videoUrl;
6
+ const videoElem = message.videoElem;
7
+ const videoUrl = (videoElem === null || videoElem === void 0 ? void 0 : videoElem.videoUrl) || "";
8
+ const posterUrl = (videoElem === null || videoElem === void 0 ? void 0 : videoElem.snapshotUrl) || "";
13
9
  const isSending = message.status === MessageStatus.Sending;
14
- const minStyle = {
15
- minHeight: `${adaptedHight}px`,
16
- minWidth: `${adaptedWidth}px`,
17
- };
18
- return (_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "relative max-w-[200px]", style: minStyle, children: _jsx("video", { className: "rounded-md", src: sourceUrl, controls: true }) }) }));
10
+ return (_jsx("div", { className: "-mx-1", children: _jsx(Spin, { spinning: isSending, children: _jsx("video", { src: videoUrl, poster: posterUrl, controls: true, className: "h-[150px] w-[215px] rounded-xl object-cover" }) }) }));
19
11
  };
20
12
  export default VideoMessageItem;
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ import type { TFunction } from "i18next";
3
+ export declare const downloadImage: (imageUrl?: string) => void;
4
+ export declare const renderImagePreviewToolbar: (originalNode: ReactNode, imageUrl: string | undefined, t: TFunction) => ReactNode;
5
+ //# sourceMappingURL=imagePreviewToolbar.d.ts.map