@droppii-org/chat-sdk 0.1.50 → 0.1.52

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 (99) 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/BizInboxHeader.js +1 -1
  6. package/dist/components/biz-inbox/BizThreadAvatar.js +2 -2
  7. package/dist/components/biz-inbox/BizThreadCard.d.ts.map +1 -1
  8. package/dist/components/biz-inbox/BizThreadCard.js +3 -11
  9. package/dist/components/biz-inbox/BizThreadTitle.js +1 -1
  10. package/dist/components/biz-thread-detail/BizMessageList.js +2 -2
  11. package/dist/components/biz-thread-detail/BizMoreOptionItem.js +2 -2
  12. package/dist/components/biz-thread-detail/BizThreadDetailHeader.js +2 -2
  13. package/dist/components/biz-thread-detail/BizThreadDetailInput.d.ts.map +1 -1
  14. package/dist/components/biz-thread-detail/BizThreadDetailInput.js +12 -21
  15. package/dist/components/biz-thread-detail/index.d.ts +0 -4
  16. package/dist/components/biz-thread-detail/index.d.ts.map +1 -1
  17. package/dist/components/biz-thread-detail/index.js +0 -2
  18. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  19. package/dist/components/conversation/ConversationBySessionItem.js +6 -5
  20. package/dist/components/conversation/ThreadCard.d.ts +14 -0
  21. package/dist/components/conversation/ThreadCard.d.ts.map +1 -0
  22. package/dist/components/conversation/ThreadCard.js +16 -0
  23. package/dist/components/media-collection/ImageCollection.d.ts.map +1 -1
  24. package/dist/components/media-collection/ImageCollection.js +3 -17
  25. package/dist/components/message/MessageList.d.ts.map +1 -1
  26. package/dist/components/message/MessageList.js +1 -7
  27. package/dist/components/message/footer/ComposerTextSyncPlugin.d.ts +6 -0
  28. package/dist/components/message/footer/ComposerTextSyncPlugin.d.ts.map +1 -0
  29. package/dist/components/message/footer/ComposerTextSyncPlugin.js +16 -0
  30. package/dist/components/message/footer/FilePreview.d.ts +3 -2
  31. package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
  32. package/dist/components/message/footer/FilePreview.js +61 -21
  33. package/dist/components/message/footer/LinkInputPreview.d.ts +7 -0
  34. package/dist/components/message/footer/LinkInputPreview.d.ts.map +1 -0
  35. package/dist/components/message/footer/LinkInputPreview.js +16 -0
  36. package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
  37. package/dist/components/message/footer/MediaActions.js +8 -5
  38. package/dist/components/message/footer/index.d.ts.map +1 -1
  39. package/dist/components/message/footer/index.js +48 -35
  40. package/dist/components/message/item/FileMessage.d.ts +2 -2
  41. package/dist/components/message/item/FileMessage.d.ts.map +1 -1
  42. package/dist/components/message/item/FileMessage.js +20 -12
  43. package/dist/components/message/item/ImageMessage.d.ts.map +1 -1
  44. package/dist/components/message/item/ImageMessage.js +17 -15
  45. package/dist/components/message/item/LinkCard.d.ts +7 -0
  46. package/dist/components/message/item/LinkCard.d.ts.map +1 -0
  47. package/dist/components/message/item/LinkCard.js +17 -0
  48. package/dist/components/message/item/LinkText.d.ts +6 -0
  49. package/dist/components/message/item/LinkText.d.ts.map +1 -0
  50. package/dist/components/message/item/LinkText.js +13 -0
  51. package/dist/components/message/item/MergeImageMessage.d.ts +7 -0
  52. package/dist/components/message/item/MergeImageMessage.d.ts.map +1 -0
  53. package/dist/components/message/item/MergeImageMessage.js +30 -0
  54. package/dist/components/message/item/MessageBubble.d.ts +13 -0
  55. package/dist/components/message/item/MessageBubble.d.ts.map +1 -0
  56. package/dist/components/message/item/MessageBubble.js +23 -0
  57. package/dist/components/message/item/RevokeMessage.js +1 -1
  58. package/dist/components/message/item/TextMessage.d.ts.map +1 -1
  59. package/dist/components/message/item/TextMessage.js +20 -9
  60. package/dist/components/message/item/UrlTextMessage.d.ts.map +1 -1
  61. package/dist/components/message/item/UrlTextMessage.js +19 -17
  62. package/dist/components/message/item/VideoMessage.d.ts.map +1 -1
  63. package/dist/components/message/item/VideoMessage.js +10 -12
  64. package/dist/components/message/item/imagePreviewToolbar.d.ts +5 -0
  65. package/dist/components/message/item/imagePreviewToolbar.d.ts.map +1 -0
  66. package/dist/components/message/item/imagePreviewToolbar.js +14 -0
  67. package/dist/components/message/item/index.d.ts +2 -1
  68. package/dist/components/message/item/index.d.ts.map +1 -1
  69. package/dist/components/message/item/index.js +40 -44
  70. package/dist/components/message/item/mediaSize.d.ts +16 -0
  71. package/dist/components/message/item/mediaSize.d.ts.map +1 -0
  72. package/dist/components/message/item/mediaSize.js +20 -0
  73. package/dist/components/message/item/textStyle.d.ts +2 -0
  74. package/dist/components/message/item/textStyle.d.ts.map +1 -0
  75. package/dist/components/message/item/textStyle.js +1 -0
  76. package/dist/components/thread/ManualAssignPopover.js +1 -1
  77. package/dist/hooks/message/useSendMessage.d.ts +3 -10
  78. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  79. package/dist/hooks/message/useSendMessage.js +27 -44
  80. package/dist/styles/global.css +1 -1
  81. package/dist/tsconfig.tsbuildinfo +1 -1
  82. package/dist/utils/bizConversation.d.ts +1 -1
  83. package/dist/utils/bizConversation.js +2 -2
  84. package/dist/utils/bizMessage.d.ts +1 -14
  85. package/dist/utils/bizMessage.d.ts.map +1 -1
  86. package/dist/utils/bizMessage.js +0 -26
  87. package/dist/utils/common.d.ts +2 -1
  88. package/dist/utils/common.d.ts.map +1 -1
  89. package/dist/utils/common.js +9 -3
  90. package/dist/utils/fileValidation.d.ts +1 -0
  91. package/dist/utils/fileValidation.d.ts.map +1 -1
  92. package/dist/utils/fileValidation.js +10 -17
  93. package/dist/utils/link.d.ts +13 -0
  94. package/dist/utils/link.d.ts.map +1 -0
  95. package/dist/utils/link.js +77 -0
  96. package/dist/utils/openLink.d.ts +3 -0
  97. package/dist/utils/openLink.d.ts.map +1 -0
  98. package/dist/utils/openLink.js +19 -0
  99. package/package.json +1 -1
@@ -1,16 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback } from "react";
3
+ import { useEffect, useState } from "react";
4
4
  import { useMessageFooterContext } from ".";
5
- import { Button } from "antd";
6
5
  import { Icon } from "../../../components/icon";
7
6
  import { documentIcon } from "../../../assets/svg";
8
- const documentTypes = [
9
- "application/pdf",
10
- "application/msword",
11
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
12
- ];
7
+ import { DOCUMENT_MIME_TYPES } from "../../../utils/fileValidation";
8
+ import { formatDuration } from "../../../utils/common";
13
9
  export const shortenFileName = (name, options = {}) => {
10
+ if (!name)
11
+ return "";
14
12
  const { maxLength = 20, keepStart = 8, keepEnd = 3 } = options;
15
13
  if (name.length <= maxLength)
16
14
  return name;
@@ -24,23 +22,65 @@ export const shortenFileName = (name, options = {}) => {
24
22
  const end = base.slice(-endLen);
25
23
  return `${start}...${end}${ext}`;
26
24
  };
27
- export function FilePreviewList({ files, onRemove, }) {
28
- const renderFilePreview = useCallback((file) => {
29
- var _a, _b, _c;
30
- const isDocument = documentTypes.includes(((_a = file === null || file === void 0 ? void 0 : file.originFileObj) === null || _a === void 0 ? void 0 : _a.type) || "");
31
- const isVideo = (_b = file.type) === null || _b === void 0 ? void 0 : _b.startsWith("video/");
32
- let src = file.url;
33
- if (!src && file.originFileObj) {
34
- src = URL.createObjectURL(file.originFileObj);
35
- }
36
- if (isDocument) {
37
- return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2", children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(((_c = file === null || file === void 0 ? void 0 : file.originFileObj) === null || _c === void 0 ? void 0 : _c.name) || "") }), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemove(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) })] }, file.uid));
25
+ const formatFileSize = (bytes) => {
26
+ if (bytes === 0)
27
+ return "0 B";
28
+ if (bytes < 1024)
29
+ return `${bytes} B`;
30
+ const kb = bytes / 1024;
31
+ if (kb < 1024)
32
+ return `${Math.round(kb)} KB`;
33
+ return `${(kb / 1024).toFixed(1)} MB`;
34
+ };
35
+ const getFileType = (file) => {
36
+ var _a, _b, _c;
37
+ const mimeType = (_c = (_b = (_a = file.originFileObj) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : file.type) !== null && _c !== void 0 ? _c : "";
38
+ if (DOCUMENT_MIME_TYPES.includes(mimeType))
39
+ return "document";
40
+ if (mimeType.startsWith("video/"))
41
+ return "video";
42
+ return "image";
43
+ };
44
+ const RemoveButton = ({ file, onRemove }) => (_jsx("button", { type: "button", onClick: () => onRemove(file), className: "absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-gray-900", "aria-label": "X\u00F3a", children: _jsx(Icon, { icon: "close-o", size: 12, className: "text-white" }) }));
45
+ const MediaThumb = ({ file, onRemove, onVideoReady }) => {
46
+ var _a;
47
+ const [src, setSrc] = useState("");
48
+ const [duration, setDuration] = useState(null);
49
+ const isVideo = getFileType(file) === "video";
50
+ const isLoading = file.status === "uploading";
51
+ const percent = Math.round((_a = file.percent) !== null && _a !== void 0 ? _a : 0);
52
+ useEffect(() => {
53
+ var _a;
54
+ const originFile = file.originFileObj;
55
+ if (!originFile) {
56
+ setSrc((_a = file.url) !== null && _a !== void 0 ? _a : "");
57
+ return;
38
58
  }
39
- return (_jsxs("div", { className: "relative rounded-md border", children: [isVideo ? (_jsx("video", { src: src, className: "w-[48px] h-[48px] object-cover rounded-lg", autoPlay: false })) : (_jsx("img", { src: src, alt: file.name, className: "w-[48px] h-[48px] object-cover rounded-lg" })), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemove(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) }), isVideo && (_jsx(Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", icon: "play-b", size: 20, color: "white" }))] }, file.uid));
40
- }, [files]);
59
+ const url = URL.createObjectURL(originFile);
60
+ setSrc(url);
61
+ return () => URL.revokeObjectURL(url);
62
+ }, [file.originFileObj, file.url]);
63
+ const handleLoadedMetadata = (event) => {
64
+ setDuration(event.currentTarget.duration);
65
+ onVideoReady === null || onVideoReady === void 0 ? void 0 : onVideoReady();
66
+ };
67
+ return (_jsxs("div", { className: "relative h-12 w-12 shrink-0", children: [isVideo ? (_jsx("video", { src: src, className: "h-12 w-12 rounded-lg object-cover", preload: "metadata", muted: true, onLoadedMetadata: handleLoadedMetadata })) : (_jsx("img", { src: src, alt: file.name, className: "h-12 w-12 rounded-lg object-cover" })), isLoading && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-lg bg-black/60", children: _jsxs("span", { className: "text-xs font-semibold text-white", children: [percent, "%"] }) })), isVideo && duration !== null && (_jsx("div", { className: "absolute bottom-1 left-1/2 flex w-9 -translate-x-1/2 items-center justify-center rounded-xl bg-black/70 px-1 text-center text-[10px] font-normal leading-4 text-white", children: formatDuration(duration) })), _jsx(RemoveButton, { file: file, onRemove: onRemove })] }));
68
+ };
69
+ const DocumentCard = ({ file, onRemove }) => {
70
+ var _a, _b;
71
+ const isLoading = file.status === "uploading";
72
+ const percent = Math.round((_a = file.percent) !== null && _a !== void 0 ? _a : 0);
73
+ return (_jsxs("div", { className: "relative flex h-12 w-40 shrink-0 items-center gap-[7px] rounded-lg bg-chat-bubble-neutral/10 py-[4px] pl-2 pr-5", children: [_jsx("div", { className: "flex h-full w-8 shrink-0 items-center justify-center overflow-hidden [&>svg]:h-8 [&>svg]:w-8", children: documentIcon }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate text-[11px] font-normal leading-[160%] text-gray-900", children: shortenFileName(file.name) }), isLoading ? (_jsxs("span", { className: "text-[10px] leading-[160%] tracking-[0.1px] text-gray-400", children: [percent, "%"] })) : (_jsx("span", { className: "text-[10px] leading-[160%] tracking-[0.1px] text-gray-400", children: formatFileSize((_b = file.size) !== null && _b !== void 0 ? _b : 0) }))] }), _jsx(RemoveButton, { file: file, onRemove: onRemove })] }));
74
+ };
75
+ export function FilePreviewList({ files, onRemove, onVideoReady, }) {
41
76
  if (files.length === 0)
42
77
  return null;
43
- return (_jsx("div", { className: "overflow-x-auto mb-[-4px]", children: _jsx("div", { className: "border-b py-2 px-4", children: _jsx("div", { className: "flex items-center gap-2", children: files.map((file) => renderFilePreview(file)) }) }) }));
78
+ return (_jsx("div", { className: "flex items-center gap-3 self-stretch overflow-x-auto px-2 pb-2 pt-3", children: files.map((file) => {
79
+ if (getFileType(file) === "document") {
80
+ return (_jsx(DocumentCard, { file: file, onRemove: onRemove }, file.uid));
81
+ }
82
+ return (_jsx(MediaThumb, { file: file, onRemove: onRemove, onVideoReady: onVideoReady }, file.uid));
83
+ }) }));
44
84
  }
45
85
  const FilePreview = () => {
46
86
  const { listUploadFiles, setListUploadFiles } = useMessageFooterContext();
@@ -0,0 +1,7 @@
1
+ export interface LinkInputPreviewProps {
2
+ url: string;
3
+ onRemove: () => void;
4
+ }
5
+ declare const LinkInputPreview: ({ url, onRemove }: LinkInputPreviewProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default LinkInputPreview;
7
+ //# sourceMappingURL=LinkInputPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkInputPreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/LinkInputPreview.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,mBAAmB,qBAAqB,4CAoEjE,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Icon } from "../../../components/icon";
4
+ import { useFetchUrlMetadata } from "../../../hooks/common/useFetchUrlMetadata";
5
+ import { getHostLabel } from "../../../utils/link";
6
+ import { openLink } from "../../../utils/openLink";
7
+ import { useTranslation } from "react-i18next";
8
+ const LinkInputPreview = ({ url, onRemove }) => {
9
+ const { t } = useTranslation("biz-inbox");
10
+ const { data, isLoading, isError } = useFetchUrlMetadata(url);
11
+ const handleOpen = () => {
12
+ openLink(url, t);
13
+ };
14
+ return (_jsxs("div", { "data-testid": "link-input-preview", className: "flex w-full shrink-0 items-center gap-2 overflow-hidden bg-blue-25 pr-2", children: [_jsxs("button", { type: "button", onClick: handleOpen, className: "flex min-w-0 flex-1 items-center gap-2 py-0 pl-0 text-left", children: [_jsx("div", { className: "flex h-[51px] w-[51px] shrink-0 items-center justify-center overflow-hidden bg-blue-25", children: isLoading ? (_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-primary-400 border-t-transparent" })) : (data === null || data === void 0 ? void 0 : data.image) ? (_jsx("img", { src: data.image, alt: "", className: "h-full w-full object-cover" })) : (_jsx(Icon, { icon: "link-alt-b", size: 24, className: "text-primary-400" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [isLoading ? (_jsx("p", { className: "truncate text-[14px] font-medium leading-[1.6] text-gray-900", children: t("link_preview.loading") })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "truncate text-[14px] font-medium leading-[1.6] text-gray-900", children: (data === null || data === void 0 ? void 0 : data.title) || getHostLabel(url) }), ((data === null || data === void 0 ? void 0 : data.description) || !(data === null || data === void 0 ? void 0 : data.title)) && (_jsx("p", { className: "truncate text-[13px] leading-[1.6] text-gray-400", children: (data === null || data === void 0 ? void 0 : data.description) || getHostLabel(url) }))] })), isError && !isLoading && (_jsx("p", { className: "truncate text-[13px] leading-[1.6] text-gray-400", children: getHostLabel(url) }))] })] }), _jsx("button", { type: "button", onClick: onRemove, "data-testid": "link-input-preview-remove", className: "flex h-4 w-4 shrink-0 items-center justify-center", "aria-label": t("link_preview.remove"), children: _jsx(Icon, { icon: "close-b", size: 16, className: "text-gray-900" }) })] }));
15
+ };
16
+ export default LinkInputPreview;
@@ -1 +1 @@
1
- {"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAoD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,GACvB,EAAE,iBAAiB,2CAwKnB"}
1
+ {"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAqD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,GACvB,EAAE,iBAAiB,2CAyKnB"}
@@ -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-gray-200/20 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-gray-900", children: shortenFileName(fileName) }), _jsx("span", { className: "truncate text-[13px] leading-[160%] text-gray-600", 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-gray-900" }) }), _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-gray-900" }) })] })] }) }));
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;AAMrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAyCrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,23 +1,25 @@
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";
6
+ import { getImageMessageMinStyle } from "./mediaSize";
5
7
  const ImageMessageItem = (props) => {
6
- var _a, _b, _c, _d;
8
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
7
9
  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);
10
+ const { t } = useTranslation();
11
+ const pictureElem = message.pictureElem;
12
+ 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) || "";
13
+ const bigUrl = ((_c = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.bigPicture) === null || _c === void 0 ? void 0 : _c.url) || sourceUrl;
14
+ const minStyle = getImageMessageMinStyle({
15
+ imageWidth: (_e = (_d = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.sourcePicture) === null || _d === void 0 ? void 0 : _d.width) !== null && _e !== void 0 ? _e : (_f = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.snapshotPicture) === null || _f === void 0 ? void 0 : _f.width,
16
+ imageHeight: (_h = (_g = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.sourcePicture) === null || _g === void 0 ? void 0 : _g.height) !== null && _h !== void 0 ? _h : (_j = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.snapshotPicture) === null || _j === void 0 ? void 0 : _j.height,
17
+ snapshotHeight: (_k = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.snapshotPicture) === null || _k === void 0 ? void 0 : _k.height,
18
+ });
16
19
  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, {}) }) }) }) }));
20
+ return (_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "relative max-w-[200px]", style: minStyle, children: _jsx(Image, { src: sourceUrl, width: minStyle.width, className: "cursor-pointer rounded-xl object-contain", preview: {
21
+ src: bigUrl,
22
+ 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); },
23
+ }, placeholder: _jsx("div", { className: "flex items-center justify-center rounded-xl bg-black/10", style: minStyle, children: _jsx(Spin, {}) }) }) }) }));
22
24
  };
23
25
  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-gray-900/10 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-gray-900", children: title })), _jsx("p", { className: "truncate text-[12px] font-medium leading-[1.6] tracking-[0.12px] text-primary-400", children: getHostLabel(url) }), description && (_jsx("p", { className: "line-clamp-2 text-[12px] leading-[1.6] text-gray-600", 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-primary-400 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;AA4CD,QAAA,MAAM,aAAa,GAAI,2EAQpB,kBAAkB,4CAsCpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,23 @@
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: clsx("flex shrink-0 items-center justify-center", isInternal
7
+ ? "text-chat-internal"
8
+ : isMine
9
+ ? "text-chat-bubble-mine"
10
+ : "text-chat-bubble-neutral"), 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: "currentColor", fillOpacity: "0.1" }) }) }) }));
11
+ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, }) => {
12
+ const bubbleClass = clsx("relative max-w-full p-2", showTip
13
+ ? isMine
14
+ ? "rounded-[20px] rounded-br-none"
15
+ : "rounded-[20px] rounded-bl-none"
16
+ : "rounded-[20px]", isInternal
17
+ ? "bg-chat-internal/10"
18
+ : isMine
19
+ ? "bg-chat-bubble-mine/10"
20
+ : "bg-chat-bubble-neutral/10");
21
+ 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-gray-400", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTip && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal }))] }));
22
+ };
23
+ 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;