@droppii-org/chat-sdk 0.1.47 → 0.1.49

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 (25) hide show
  1. package/dist/components/biz-thread-detail/BizAttachmentPreview.d.ts +9 -0
  2. package/dist/components/biz-thread-detail/BizAttachmentPreview.d.ts.map +1 -0
  3. package/dist/components/biz-thread-detail/BizAttachmentPreview.js +74 -0
  4. package/dist/components/biz-thread-detail/BizImageViewer.d.ts +8 -0
  5. package/dist/components/biz-thread-detail/BizImageViewer.d.ts.map +1 -0
  6. package/dist/components/biz-thread-detail/BizImageViewer.js +35 -0
  7. package/dist/components/biz-thread-detail/BizMessageContent.d.ts.map +1 -1
  8. package/dist/components/biz-thread-detail/BizMessageContent.js +46 -1
  9. package/dist/components/biz-thread-detail/BizMoreOptionPanel.d.ts +2 -1
  10. package/dist/components/biz-thread-detail/BizMoreOptionPanel.d.ts.map +1 -1
  11. package/dist/components/biz-thread-detail/BizMoreOptionPanel.js +6 -2
  12. package/dist/components/biz-thread-detail/BizThreadDetailInput.d.ts.map +1 -1
  13. package/dist/components/biz-thread-detail/BizThreadDetailInput.js +32 -7
  14. package/dist/components/biz-thread-detail/index.d.ts +2 -2
  15. package/dist/components/biz-thread-detail/index.d.ts.map +1 -1
  16. package/dist/components/biz-thread-detail/index.js +1 -1
  17. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  18. package/dist/hooks/message/useSendMessage.js +57 -32
  19. package/dist/screens/biz-thread-detail/index.js +1 -1
  20. package/dist/styles/global.css +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/dist/utils/fileValidation.d.ts +2 -0
  23. package/dist/utils/fileValidation.d.ts.map +1 -1
  24. package/dist/utils/fileValidation.js +30 -0
  25. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ import { UploadFile } from "antd";
2
+ export interface BizAttachmentPreviewProps {
3
+ files: UploadFile[];
4
+ onRemove: (uid: string) => void;
5
+ onVideoReady?: () => void;
6
+ }
7
+ declare const BizAttachmentPreview: ({ files, onRemove, onVideoReady, }: BizAttachmentPreviewProps) => import("react/jsx-runtime").JSX.Element | null;
8
+ export default BizAttachmentPreview;
9
+ //# sourceMappingURL=BizAttachmentPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizAttachmentPreview.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizAttachmentPreview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAmJlC,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,QAAA,MAAM,oBAAoB,GAAI,oCAI3B,yBAAyB,mDAuB3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { Icon } from "../../components/icon";
5
+ import { documentIcon } from "../../assets/svg";
6
+ import { shortenFileName } from "../../components/message/footer/FilePreview";
7
+ import { DOCUMENT_MIME_TYPES } from "../../utils/fileValidation";
8
+ const formatDuration = (seconds) => {
9
+ const minutes = Math.floor(seconds / 60);
10
+ const secs = Math.floor(seconds % 60);
11
+ return `${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
12
+ };
13
+ const formatFileSize = (bytes) => {
14
+ if (bytes === 0)
15
+ return "0 B";
16
+ if (bytes < 1024)
17
+ return `${bytes} B`;
18
+ const kb = bytes / 1024;
19
+ if (kb < 1024)
20
+ return `${Math.round(kb)} KB`;
21
+ return `${(kb / 1024).toFixed(1)} MB`;
22
+ };
23
+ const getFileType = (file) => {
24
+ var _a, _b, _c;
25
+ 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 : "";
26
+ if (DOCUMENT_MIME_TYPES.includes(mimeType))
27
+ return "document";
28
+ if (mimeType.startsWith("video/"))
29
+ return "video";
30
+ return "image";
31
+ };
32
+ const RemoveButton = ({ uid, onRemove }) => (_jsx("button", { type: "button", onClick: () => onRemove(uid), className: "absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-[#393E40]", "aria-label": "X\u00F3a", children: _jsx(Icon, { icon: "close-o", size: 12, className: "text-white" }) }));
33
+ const MediaThumb = ({ file, onRemove, onVideoReady }) => {
34
+ var _a;
35
+ const [src, setSrc] = useState("");
36
+ const [duration, setDuration] = useState(null);
37
+ const isVideo = getFileType(file) === "video";
38
+ const isLoading = file.status === "uploading";
39
+ const percent = Math.round((_a = file.percent) !== null && _a !== void 0 ? _a : 0);
40
+ useEffect(() => {
41
+ var _a;
42
+ const originFile = file.originFileObj;
43
+ if (!originFile) {
44
+ setSrc((_a = file.url) !== null && _a !== void 0 ? _a : "");
45
+ return;
46
+ }
47
+ const url = URL.createObjectURL(originFile);
48
+ setSrc(url);
49
+ return () => URL.revokeObjectURL(url);
50
+ }, [file.originFileObj, file.url]);
51
+ const handleLoadedMetadata = (event) => {
52
+ setDuration(event.currentTarget.duration);
53
+ onVideoReady === null || onVideoReady === void 0 ? void 0 : onVideoReady();
54
+ };
55
+ 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, { uid: file.uid, onRemove: onRemove })] }));
56
+ };
57
+ const DocumentCard = ({ file, onRemove }) => {
58
+ var _a, _b;
59
+ const isLoading = file.status === "uploading";
60
+ const percent = Math.round((_a = file.percent) !== null && _a !== void 0 ? _a : 0);
61
+ return (_jsxs("div", { className: "relative flex h-12 w-40 shrink-0 items-center gap-[7px] rounded-lg bg-[rgba(131,137,157,0.08)] 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-[#393E40]", children: shortenFileName(file.name) }), isLoading ? (_jsxs("span", { className: "text-[10px] leading-[160%] tracking-[0.1px] text-[#747B7E]", children: [percent, "%"] })) : (_jsx("span", { className: "text-[10px] leading-[160%] tracking-[0.1px] text-[#747B7E]", children: formatFileSize((_b = file.size) !== null && _b !== void 0 ? _b : 0) }))] }), _jsx(RemoveButton, { uid: file.uid, onRemove: onRemove })] }));
62
+ };
63
+ const BizAttachmentPreview = ({ files, onRemove, onVideoReady, }) => {
64
+ if (files.length === 0)
65
+ return null;
66
+ return (_jsx("div", { className: "flex items-center gap-3 self-stretch overflow-x-auto px-2 pb-2 pt-3", children: files.map((file) => {
67
+ const fileType = getFileType(file);
68
+ if (fileType === "document") {
69
+ return (_jsx(DocumentCard, { file: file, onRemove: onRemove }, file.uid));
70
+ }
71
+ return (_jsx(MediaThumb, { file: file, onRemove: onRemove, onVideoReady: onVideoReady }, file.uid));
72
+ }) }));
73
+ };
74
+ export default BizAttachmentPreview;
@@ -0,0 +1,8 @@
1
+ interface BizImageViewerProps {
2
+ images: string[];
3
+ initialIndex?: number;
4
+ onClose: () => void;
5
+ }
6
+ declare const BizImageViewer: ({ images, initialIndex, onClose, }: BizImageViewerProps) => import("react").ReactPortal;
7
+ export default BizImageViewer;
8
+ //# sourceMappingURL=BizImageViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BizImageViewer.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizImageViewer.tsx"],"names":[],"mappings":"AAOA,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,cAAc,GAAI,oCAIrB,mBAAmB,gCA2ErB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { createPortal } from "react-dom";
5
+ import clsx from "clsx";
6
+ import { Icon } from "../../components/icon";
7
+ const BizImageViewer = ({ images, initialIndex = 0, onClose, }) => {
8
+ var _a;
9
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
10
+ useEffect(() => {
11
+ const handleKeyDown = (e) => {
12
+ if (e.key === "Escape")
13
+ onClose();
14
+ if (e.key === "ArrowLeft")
15
+ setCurrentIndex((i) => Math.max(0, i - 1));
16
+ if (e.key === "ArrowRight")
17
+ setCurrentIndex((i) => Math.min(images.length - 1, i + 1));
18
+ };
19
+ document.addEventListener("keydown", handleKeyDown);
20
+ return () => document.removeEventListener("keydown", handleKeyDown);
21
+ }, [images.length, onClose]);
22
+ const handleDownload = () => {
23
+ const url = images[currentIndex];
24
+ const filename = url.substring(url.lastIndexOf("/") + 1) || "image";
25
+ const link = document.createElement("a");
26
+ link.href = url;
27
+ link.download = filename;
28
+ document.body.appendChild(link);
29
+ link.click();
30
+ document.body.removeChild(link);
31
+ };
32
+ const container = (_a = document.querySelector('[data-testid="biz-thread-detail-root"]')) !== null && _a !== void 0 ? _a : document.body;
33
+ return createPortal(_jsxs("div", { className: "absolute inset-0 z-[999] flex flex-col bg-black", children: [_jsxs("div", { className: "flex items-center justify-between px-4 pt-12 pb-4", children: [_jsx("button", { className: "flex h-10 w-10 items-center justify-center text-white text-xl", onClick: onClose, children: _jsx(Icon, { icon: "close-b", size: 24 }) }), _jsx("button", { className: "flex h-10 w-10 items-center justify-center text-white text-xl", onClick: handleDownload, children: _jsx(Icon, { icon: "download-o", size: 24 }) })] }), _jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center overflow-hidden px-4", children: _jsx("img", { src: images[currentIndex], alt: "", className: "max-h-full max-w-full rounded-[24px] object-contain" }) }), _jsxs("div", { className: "flex flex-col items-center gap-2 pb-8 pt-4", children: [_jsxs("span", { className: "text-sm text-white", children: [currentIndex + 1, "/", images.length] }), _jsx("div", { className: "flex gap-1.5", children: images.map((_, i) => (_jsx("button", { className: clsx("h-1.5 w-1.5 rounded-full transition-colors", i === currentIndex ? "bg-white" : "bg-white/30"), onClick: () => setCurrentIndex(i) }, i))) })] })] }), container);
34
+ };
35
+ export default BizImageViewer;
@@ -1 +1 @@
1
- {"version":3,"file":"BizMessageContent.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizMessageContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAKnE,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,iBAAiB,GAAI,aAAa,sBAAsB,mDAoC7D,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"BizMessageContent.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizMessageContent.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAA8B,MAAM,yBAAyB,CAAC;AAOlF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,WAAW,CAAC;CACtB;AAuGD,QAAA,MAAM,iBAAiB,GAAI,aAAa,sBAAsB,mDA4C7D,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -1,11 +1,56 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { MessageType } from "@openim/wasm-client-sdk";
3
+ import { useState } from "react";
4
+ import clsx from "clsx";
5
+ import { MessageStatus, MessageType } from "@openim/wasm-client-sdk";
6
+ import { Image, Spin } from "antd";
4
7
  import BizLinkMessageCard from "./BizLinkMessageCard";
5
8
  import BizLinkTextContent from "./BizLinkTextContent";
9
+ import BizImageViewer from "./BizImageViewer";
6
10
  import { extractBizLinkUrls } from "../../utils/bizLink";
11
+ const MERGE_IMAGE_GRID_COLS_CLASS = {
12
+ 1: "grid-cols-[repeat(1,100px)]",
13
+ 2: "grid-cols-[repeat(2,100px)]",
14
+ 3: "grid-cols-[repeat(3,100px)]",
15
+ };
16
+ const BizPictureMessage = ({ message }) => {
17
+ var _a, _b, _c;
18
+ const [viewerVisible, setViewerVisible] = useState(false);
19
+ const pictureElem = message.pictureElem;
20
+ 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) || "";
21
+ const bigUrl = ((_c = pictureElem === null || pictureElem === void 0 ? void 0 : pictureElem.bigPicture) === null || _c === void 0 ? void 0 : _c.url) || sourceUrl;
22
+ const isSending = message.status === MessageStatus.Sending;
23
+ return (_jsxs("div", { className: "-mx-1", children: [_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "cursor-pointer", onClick: () => setViewerVisible(true), children: _jsx(Image, { src: sourceUrl, width: 205, height: 205, className: "rounded-xl object-cover", preview: false, placeholder: _jsx("div", { className: "flex h-[205px] w-[205px] items-center justify-center rounded-xl bg-black/10", children: _jsx(Spin, {}) }) }) }) }), viewerVisible && (_jsx(BizImageViewer, { images: [bigUrl], onClose: () => setViewerVisible(false) }))] }));
24
+ };
25
+ const BizMergeImageMessage = ({ message }) => {
26
+ var _a, _b;
27
+ const [viewerIndex, setViewerIndex] = useState(null);
28
+ const subMessages = (_b = (_a = message.mergeElem) === null || _a === void 0 ? void 0 : _a.multiMessage) !== null && _b !== void 0 ? _b : [];
29
+ const isSending = message.status === MessageStatus.Sending;
30
+ const cols = Math.min(subMessages.length, 3);
31
+ const imageUrls = subMessages.map((item) => {
32
+ var _a, _b, _c, _d, _e, _f;
33
+ const sourceUrl = ((_b = (_a = item.pictureElem) === null || _a === void 0 ? void 0 : _a.sourcePicture) === null || _b === void 0 ? void 0 : _b.url) ||
34
+ ((_d = (_c = item.pictureElem) === null || _c === void 0 ? void 0 : _c.snapshotPicture) === null || _d === void 0 ? void 0 : _d.url) ||
35
+ "";
36
+ return ((_f = (_e = item.pictureElem) === null || _e === void 0 ? void 0 : _e.bigPicture) === null || _f === void 0 ? void 0 : _f.url) || sourceUrl;
37
+ });
38
+ return (_jsxs("div", { className: "-mx-1", children: [_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: clsx("grid gap-1", MERGE_IMAGE_GRID_COLS_CLASS[cols]), children: subMessages.map((item, index) => {
39
+ var _a, _b, _c, _d;
40
+ const sourceUrl = ((_b = (_a = item.pictureElem) === null || _a === void 0 ? void 0 : _a.sourcePicture) === null || _b === void 0 ? void 0 : _b.url) ||
41
+ ((_d = (_c = item.pictureElem) === null || _c === void 0 ? void 0 : _c.snapshotPicture) === null || _d === void 0 ? void 0 : _d.url) ||
42
+ "";
43
+ return (_jsx("div", { className: "cursor-pointer", onClick: () => setViewerIndex(index), children: _jsx(Image, { src: sourceUrl, width: 100, height: 100, className: "rounded-xl object-cover", preview: false, placeholder: _jsx("div", { className: "flex h-[100px] w-[100px] items-center justify-center rounded-xl bg-black/10", children: _jsx(Spin, {}) }) }) }, item.clientMsgID));
44
+ }) }) }), viewerIndex !== null && (_jsx(BizImageViewer, { images: imageUrls, initialIndex: viewerIndex, onClose: () => setViewerIndex(null) }))] }));
45
+ };
7
46
  const BizMessageContent = ({ message }) => {
8
47
  var _a, _b, _c, _d, _e, _f, _g;
48
+ if (message.contentType === MessageType.PictureMessage) {
49
+ return _jsx(BizPictureMessage, { message: message });
50
+ }
51
+ if (message.contentType === MessageType.MergeMessage) {
52
+ return _jsx(BizMergeImageMessage, { message: message });
53
+ }
9
54
  const text = (_e = (_d = (_b = (_a = message.textElem) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : (_c = message.urlTextElem) === null || _c === void 0 ? void 0 : _c.content) !== null && _d !== void 0 ? _d : message.content) !== null && _e !== void 0 ? _e : "";
10
55
  const hasLinks = text ? extractBizLinkUrls(text).length > 0 : false;
11
56
  if (message.contentType === MessageType.UrlTextMessage) {
@@ -3,7 +3,8 @@ export interface BizMoreOptionPanelProps {
3
3
  disabled?: boolean;
4
4
  onSelectOption?: (option: BizMoreOptionConfig) => void;
5
5
  onTriggerMediaInput?: () => void;
6
+ onTriggerDocumentInput?: () => void;
6
7
  }
7
- declare const BizMoreOptionPanel: ({ disabled, onSelectOption, onTriggerMediaInput, }: BizMoreOptionPanelProps) => import("react/jsx-runtime").JSX.Element;
8
+ declare const BizMoreOptionPanel: ({ disabled, onSelectOption, onTriggerMediaInput, onTriggerDocumentInput, }: BizMoreOptionPanelProps) => import("react/jsx-runtime").JSX.Element;
8
9
  export default BizMoreOptionPanel;
9
10
  //# sourceMappingURL=BizMoreOptionPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BizMoreOptionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizMoreOptionPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACvD,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;CAClC;AAED,QAAA,MAAM,kBAAkB,GAAI,oDAIzB,uBAAuB,4CA0BzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"BizMoreOptionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizMoreOptionPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACvD,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,kBAAkB,GAAI,4EAKzB,uBAAuB,4CA8BzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -3,14 +3,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useCallback } from "react";
4
4
  import { BIZ_MORE_OPTIONS, } from "../../constants/bizMoreOption";
5
5
  import BizMoreOptionItem from "./BizMoreOptionItem";
6
- const BizMoreOptionPanel = ({ disabled = false, onSelectOption, onTriggerMediaInput, }) => {
6
+ const BizMoreOptionPanel = ({ disabled = false, onSelectOption, onTriggerMediaInput, onTriggerDocumentInput, }) => {
7
7
  const handleSelectOption = useCallback((option) => {
8
8
  if (option.labelKey === "thread_detail.more_option.media") {
9
9
  onTriggerMediaInput === null || onTriggerMediaInput === void 0 ? void 0 : onTriggerMediaInput();
10
10
  return;
11
11
  }
12
+ if (option.labelKey === "thread_detail.more_option.document") {
13
+ onTriggerDocumentInput === null || onTriggerDocumentInput === void 0 ? void 0 : onTriggerDocumentInput();
14
+ return;
15
+ }
12
16
  onSelectOption === null || onSelectOption === void 0 ? void 0 : onSelectOption(option);
13
- }, [onSelectOption, onTriggerMediaInput]);
17
+ }, [onSelectOption, onTriggerMediaInput, onTriggerDocumentInput]);
14
18
  return (_jsx("div", { "data-testid": "biz-more-option-panel", className: "grid grid-cols-4 gap-y-5 bg-white py-5", children: BIZ_MORE_OPTIONS.map((option) => (_jsx(BizMoreOptionItem, { option: Object.assign(Object.assign({}, option), { enabled: option.enabled && !disabled }), onSelect: handleSelectOption }, option.labelKey))) }));
15
19
  };
16
20
  export default BizMoreOptionPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"BizThreadDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizThreadDetailInput.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,oBAAoB,GAAI,gDAI3B,yBAAyB,4CA4V3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"BizThreadDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizThreadDetailInput.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,QAAA,MAAM,oBAAoB,GAAI,gDAI3B,yBAAyB,4CAkY3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -13,8 +13,8 @@ import useBizMessageDraftStore from "../../store/bizMessageDraft";
13
13
  import { resolveBizAvatarUrl, resolveBizDisplayName, } from "../../utils/bizConversation";
14
14
  import { emit } from "../../utils/events";
15
15
  import { extractBizLinkUrls } from "../../utils/bizLink";
16
- import { processAndValidateFiles } from "../../utils/fileValidation";
17
- import BizImagePreview from "./BizImagePreview";
16
+ import { processAndValidateDocuments, processAndValidateFiles, } from "../../utils/fileValidation";
17
+ import BizAttachmentPreview from "./BizAttachmentPreview";
18
18
  import BizLinkInputPreview from "./BizLinkInputPreview";
19
19
  import BizMoreOptionPanel from "./BizMoreOptionPanel";
20
20
  const MAX_LINES = 5;
@@ -42,13 +42,14 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
42
42
  "",
43
43
  }),
44
44
  }), [bizConversation, conversationData, t]);
45
- const { sendTextMessage } = useSendMessage(sendMessageOptions);
45
+ const { sendTextMessage, sendMergeMessage } = useSendMessage(sendMessageOptions);
46
46
  const userID = useAuthStore((state) => state.userID);
47
47
  const setDraft = useBizMessageDraftStore((state) => state.setDraft);
48
48
  const clearDraft = useBizMessageDraftStore((state) => state.clearDraft);
49
49
  const containerRef = useRef(null);
50
50
  const textareaRef = useRef(null);
51
51
  const mediaInputRef = useRef(null);
52
+ const documentInputRef = useRef(null);
52
53
  const [value, setValue] = useState("");
53
54
  const [listUploadFiles, setListUploadFiles] = useState([]);
54
55
  const [isVideoLoading, setIsVideoLoading] = useState(false);
@@ -64,7 +65,11 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
64
65
  useEffect(() => {
65
66
  setPreviewDismissed(false);
66
67
  }, [previewUrl]);
67
- const canSend = value.trim().length > 0 && !isSending && isConversationReady && !isVideoLoading;
68
+ const hasFiles = listUploadFiles.length > 0;
69
+ const canSend = (value.trim().length > 0 || hasFiles) &&
70
+ !isSending &&
71
+ isConversationReady &&
72
+ !isVideoLoading;
68
73
  const getTextareaHeights = useCallback(() => {
69
74
  const textarea = textareaRef.current;
70
75
  if (!textarea)
@@ -146,6 +151,17 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
146
151
  }
147
152
  event.target.value = "";
148
153
  }, [tCommon, listUploadFiles]);
154
+ const handleDocumentInputChange = useCallback((event) => {
155
+ var _a;
156
+ const files = Array.from((_a = event.target.files) !== null && _a !== void 0 ? _a : []);
157
+ if (files.length === 0)
158
+ return;
159
+ const validFiles = processAndValidateDocuments(files, tCommon);
160
+ if (validFiles.length > 0) {
161
+ setListUploadFiles((prev) => [...prev, ...validFiles]);
162
+ }
163
+ event.target.value = "";
164
+ }, [tCommon]);
149
165
  const handleRemoveFile = useCallback((uid) => {
150
166
  setListUploadFiles((prev) => prev.filter((f) => f.uid !== uid));
151
167
  setIsVideoLoading(false);
@@ -159,13 +175,22 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
159
175
  };
160
176
  const handleSend = async () => {
161
177
  const text = value.trim();
162
- if (!text || isSending)
178
+ if ((!text && !hasFiles) || isSending)
163
179
  return;
164
180
  setIsSending(true);
165
181
  try {
166
182
  if (onSendMessage) {
167
183
  onSendMessage(text);
168
184
  }
185
+ else if (hasFiles) {
186
+ await sendMergeMessage({
187
+ plainText: text,
188
+ richText: "",
189
+ files: listUploadFiles,
190
+ isInternal: false,
191
+ });
192
+ setListUploadFiles([]);
193
+ }
169
194
  else {
170
195
  const sent = await sendTextMessage({
171
196
  plainText: text,
@@ -221,8 +246,8 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
221
246
  event.preventDefault();
222
247
  void handleSend();
223
248
  };
224
- return (_jsxs("div", { ref: containerRef, "data-testid": "biz-thread-detail-input", className: "flex w-full shrink-0 flex-col bg-[rgba(131,137,157,0.05)]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), listUploadFiles.length > 0 && (_jsx(BizImagePreview, { files: listUploadFiles, onRemove: handleRemoveFile, onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(BizLinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "biz-thread-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-o", size: 24, className: "text-[#1B3FE4]" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-[#393E40]" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
249
+ return (_jsxs("div", { ref: containerRef, "data-testid": "biz-thread-detail-input", className: "flex w-full shrink-0 flex-col bg-[rgba(131,137,157,0.05)]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), _jsx("input", { ref: documentInputRef, type: "file", accept: ".pdf,.doc,.docx", multiple: true, className: "hidden", onChange: handleDocumentInputChange }), listUploadFiles.length > 0 && (_jsx(BizAttachmentPreview, { files: listUploadFiles, onRemove: handleRemoveFile, onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(BizLinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "biz-thread-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-o", size: 24, className: "text-[#1B3FE4]" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-[#393E40]" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
225
250
  ? "border-[#607CFB] shadow-[0px_0px_2px_rgba(27,63,228,0.5)]"
226
- : "border-[#DDDEDF]"), style: { minHeight: CONTAINER_MIN_HEIGHT }, children: _jsx("div", { className: "flex min-w-0 flex-[1_0_0] items-center gap-2 self-stretch px-3 py-1", children: _jsx("textarea", { ref: textareaRef, value: value, rows: 1, onChange: (event) => handleChange(event.target.value), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "biz-thread-detail-message-input", className: "biz-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-[#747B7E] focus:border-0 focus:outline-none focus:ring-0" }) }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "biz-thread-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-[#002BEB]" : "bg-[#002BEB] opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(BizMoreOptionPanel, { disabled: !isConversationReady, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
251
+ : "border-[#DDDEDF]"), style: { minHeight: CONTAINER_MIN_HEIGHT }, children: _jsx("div", { className: "flex min-w-0 flex-[1_0_0] items-center gap-2 self-stretch px-3 py-1", children: _jsx("textarea", { ref: textareaRef, value: value, rows: 1, onChange: (event) => handleChange(event.target.value), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "biz-thread-detail-message-input", className: "biz-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-[#747B7E] focus:border-0 focus:outline-none focus:ring-0" }) }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "biz-thread-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-[#002BEB]" : "bg-[#002BEB] opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(BizMoreOptionPanel, { disabled: !isConversationReady, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, onTriggerDocumentInput: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
227
252
  };
228
253
  export default BizThreadDetailInput;
@@ -9,6 +9,6 @@ export { default as BizMoreOptionPanel } from "./BizMoreOptionPanel";
9
9
  export type { BizMoreOptionPanelProps } from "./BizMoreOptionPanel";
10
10
  export { default as BizMoreOptionItem } from "./BizMoreOptionItem";
11
11
  export type { BizMoreOptionItemProps } from "./BizMoreOptionItem";
12
- export { default as BizImagePreview } from "./BizImagePreview";
13
- export type { BizImagePreviewProps } from "./BizImagePreview";
12
+ export { default as BizAttachmentPreview } from "./BizAttachmentPreview";
13
+ export type { BizAttachmentPreviewProps } from "./BizAttachmentPreview";
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -4,4 +4,4 @@ export { default as BizMessageList } from "./BizMessageList";
4
4
  export { default as BizMessageItem } from "./item/BizMessageItem";
5
5
  export { default as BizMoreOptionPanel } from "./BizMoreOptionPanel";
6
6
  export { default as BizMoreOptionItem } from "./BizMoreOptionItem";
7
- export { default as BizImagePreview } from "./BizImagePreview";
7
+ export { default as BizAttachmentPreview } from "./BizAttachmentPreview";
@@ -1 +1 @@
1
- {"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,WAAW,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAWtB,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAUlC,OAAO,EAAgB,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAkBpF,eAAO,MAAM,iBAAiB,GAAI,SAAS;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,YAE0C,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,gCAanD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iBAAiB,eAAe,gCAazE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,gCAatE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,gCActE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,eAAe,WAAW,gCAc3B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B,4BAA4B,GAC5B,wCAAwC,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,eAAe,CAAC;CACxE;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,qBAAqB;4EA+OvD;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;KACrB,KAAG,OAAO,CAAC,OAAO,CAAC;oFA6CjB;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;KACrB;mCArKqB,WAAW;CA6SpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,0CAIvC;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC,sBA4BA,CAAC"}
1
+ {"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,WAAW,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAWtB,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAUlC,OAAO,EAAgB,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAkBpF,eAAO,MAAM,iBAAiB,GAAI,SAAS;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,YAE0C,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,gCAanD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iBAAiB,eAAe,gCAazE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,gCAatE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,gCActE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,eAAe,WAAW,gCAc3B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B,4BAA4B,GAC5B,wCAAwC,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,eAAe,CAAC;CACxE;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,qBAAqB;4EA+OvD;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;KACrB,KAAG,OAAO,CAAC,OAAO,CAAC;oFA6CjB;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;KACrB;mCArKqB,WAAW;CAwUpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,0CAIvC;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC,sBA4BA,CAAC"}
@@ -311,45 +311,70 @@ export const useSendMessage = (options) => {
311
311
  if (!recvID && !groupID)
312
312
  return;
313
313
  const messageList = [];
314
- for (const fileWrapper of files) {
314
+ const imageFileWrappers = files.filter((f) => { var _a; return (_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("image/"); });
315
+ const nonImageFileWrappers = files.filter((f) => { var _a; return !((_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("image/")); });
316
+ const imageMessages = [];
317
+ for (const fileWrapper of imageFileWrappers) {
318
+ const file = fileWrapper.originFileObj;
319
+ if (!file)
320
+ continue;
321
+ try {
322
+ const picInfo = await createPicBaseInfoFromFile(file);
323
+ const baseInfo = {
324
+ uuid: uuidv4(),
325
+ type: file.type,
326
+ size: file.size,
327
+ width: picInfo.width,
328
+ height: picInfo.height,
329
+ url: URL.createObjectURL(file),
330
+ };
331
+ const imageMessage = await createImageMessageByFile({
332
+ sourcePicture: baseInfo,
333
+ bigPicture: baseInfo,
334
+ snapshotPicture: baseInfo,
335
+ sourcePath: "",
336
+ file,
337
+ });
338
+ if (!imageMessage)
339
+ continue;
340
+ imageMessages.push(imageMessage);
341
+ }
342
+ catch (err) {
343
+ console.error("Lỗi xử lý ảnh:", err);
344
+ }
345
+ }
346
+ if (imageMessages.length === 1) {
347
+ messageFileStore.set(imageMessages[0].clientMsgID, {
348
+ type: "image",
349
+ file: imageFileWrappers[0].originFileObj,
350
+ });
351
+ messageList.push(imageMessages[0]);
352
+ }
353
+ else if (imageMessages.length > 1) {
354
+ imageMessages.forEach((imageMsg, index) => {
355
+ messageFileStore.set(imageMsg.clientMsgID, {
356
+ type: "image",
357
+ file: imageFileWrappers[index].originFileObj,
358
+ });
359
+ });
360
+ const mergeMsg = await createMergerMessage({
361
+ messageList: imageMessages,
362
+ title: "Album ảnh",
363
+ summaryList: imageMessages.map(() => "[Hình ảnh]"),
364
+ });
365
+ if (mergeMsg)
366
+ messageList.push(mergeMsg);
367
+ }
368
+ for (const fileWrapper of nonImageFileWrappers) {
315
369
  const file = fileWrapper.originFileObj;
316
370
  if (!file)
317
371
  continue;
318
- const isImage = file.type.startsWith("image/");
319
372
  const isVideo = file.type.startsWith("video/");
320
373
  const isDocument = file.type.startsWith("application/");
321
374
  try {
322
- if (isImage) {
323
- const picInfo = await createPicBaseInfoFromFile(file);
324
- const baseInfo = {
325
- uuid: uuidv4(),
326
- type: file.type,
327
- size: file.size,
328
- width: picInfo.width,
329
- height: picInfo.height,
330
- url: URL.createObjectURL(file),
331
- };
332
- const parsedImage = {
333
- sourcePicture: baseInfo,
334
- bigPicture: baseInfo,
335
- snapshotPicture: baseInfo,
336
- sourcePath: "",
337
- file: file,
338
- };
339
- const imageMessage = await createImageMessageByFile(parsedImage);
340
- if (!imageMessage)
341
- continue;
342
- messageFileStore.set(imageMessage.clientMsgID, {
343
- type: "image",
344
- file: file,
345
- });
346
- messageList.push(imageMessage);
347
- }
348
- else if (isVideo) {
375
+ if (isVideo) {
349
376
  const videoBaseInfo = await createVideoInfoWithThumbnail(file);
350
- const thumbFile = new File([videoBaseInfo.thumbnail], file.name + "-thumb.jpg", {
351
- type: "image/jpeg",
352
- });
377
+ const thumbFile = new File([videoBaseInfo.thumbnail], file.name + "-thumb.jpg", { type: "image/jpeg" });
353
378
  const videoMessage = await createVideoMessageByFile({
354
379
  videoPath: "",
355
380
  duration: videoBaseInfo.duration,
@@ -31,6 +31,6 @@ const DChatBizThreadDetail = (_a) => {
31
31
  if (!conversation) {
32
32
  return null;
33
33
  }
34
- return (_jsxs("div", { "data-testid": "biz-thread-detail-root", className: clsx("flex h-full w-full flex-col bg-white", className), children: [_jsx(BizThreadDetailHeader, { conversation: conversation, onBack: handleBack }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: children !== null && children !== void 0 ? children : _jsx(BizMessageList, {}) }), _jsx(BizThreadDetailInput, Object.assign({}, inputProps))] }));
34
+ return (_jsxs("div", { "data-testid": "biz-thread-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(BizThreadDetailHeader, { conversation: conversation, onBack: handleBack }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: children !== null && children !== void 0 ? children : _jsx(BizMessageList, {}) }), _jsx(BizThreadDetailInput, Object.assign({}, inputProps))] }));
35
35
  };
36
36
  export default DChatBizThreadDetail;