@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
@@ -1,4 +1,3 @@
1
1
  export * from "./document";
2
2
  export * from "./canned-response";
3
- export * from "./expand";
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
@@ -1,3 +1,2 @@
1
1
  export * from "./document";
2
2
  export * from "./canned-response";
3
- export * from "./expand";
@@ -1,3 +1,2 @@
1
1
  export * from "./document";
2
- export * from "./canned-response";
3
- export * from "./expand";
2
+ export * from "./canned-response";
@@ -1 +1 @@
1
- {"version":3,"file":"BizThreadCard.d.ts","sourceRoot":"","sources":["../../../src/components/biz-inbox/BizThreadCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAYvF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,OAAO,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAClD;AAwBD,QAAA,MAAM,aAAa,GAAI,mBAAmB,kBAAkB,4CAiE3D,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"BizThreadCard.d.ts","sourceRoot":"","sources":["../../../src/components/biz-inbox/BizThreadCard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAavF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,OAAO,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAClD;AAQD,QAAA,MAAM,aAAa,GAAI,mBAAmB,kBAAkB,4CA6C3D,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,12 +1,12 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import clsx from "clsx";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
4
3
  import dayjs from "dayjs";
5
4
  import { useTranslation } from "react-i18next";
6
5
  import useAuthStore from "../../store/auth";
7
6
  import { formatTimestamp } from "../../utils/common";
8
7
  import { parseBizLatestMessage } from "../../utils/bizMessage";
9
8
  import { resolveBizAvatarUrl, resolveBizBadgeLabel, resolveBizDisplayName, } from "../../utils/bizConversation";
9
+ import ThreadCard from "../../components/conversation/ThreadCard";
10
10
  import BizThreadAvatar from "./BizThreadAvatar";
11
11
  import BizThreadTitle from "./BizThreadTitle";
12
12
  const formatLatestMessageTime = (sendTime) => {
@@ -15,12 +15,6 @@ const formatLatestMessageTime = (sendTime) => {
15
15
  const isToday = dayjs(sendTime).isSame(dayjs(), "day");
16
16
  return formatTimestamp(sendTime, { hasTime: isToday });
17
17
  };
18
- const formatUnreadCount = (count) => (count > 99 ? "99+" : count);
19
- const UnreadBadge = ({ count }) => {
20
- const label = String(formatUnreadCount(count));
21
- const isWide = label.length > 1;
22
- return (_jsx("span", { className: `inline-flex h-5 shrink-0 items-center justify-center rounded-[32px] border-2 border-white bg-[linear-gradient(93deg,#FF7D74_0%,#F04438_100%)] text-center text-[12px] font-semibold leading-[160%] text-white px-[3px] ${isWide ? "min-w-8" : "min-w-5"}`, children: label }));
23
- };
24
18
  const BizThreadCard = ({ item, onClick }) => {
25
19
  var _a;
26
20
  const { t } = useTranslation("biz-inbox");
@@ -32,8 +26,6 @@ const BizThreadCard = ({ item, onClick }) => {
32
26
  const latestMessagePreview = parseBizLatestMessage(item.latestMsg, currentUserId, tCommon, item.peer.fullName);
33
27
  const latestMessageTime = formatLatestMessageTime(item.latestMsgSendTime || null);
34
28
  const unreadCount = (_a = item.unreadCount) !== null && _a !== void 0 ? _a : 0;
35
- return (_jsxs("button", { type: "button", onClick: () => onClick(item), "data-testid": "biz-inbox-thread-card", className: "flex w-full shrink-0 items-start gap-2 border-b border-[#EFEFEF] bg-white p-3 text-left transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]", children: [_jsx(BizThreadAvatar, { peerType: item.peer.type, chatCategory: item.chatCategory, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex min-h-[25.6px] items-center justify-between gap-1", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsx(BizThreadTitle, { peerType: item.peer.type, chatCategory: item.chatCategory, displayName: displayName, hasUnread: unreadCount > 0 }) }), latestMessageTime && (_jsx("span", { className: "shrink-0 text-[12px] font-normal leading-[160%] text-[#747B7E] [font-feature-settings:'liga'_off,'clig'_off]", children: latestMessageTime }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: clsx("min-w-0 flex-1 truncate text-[14px] leading-[160%] [font-feature-settings:'liga'_off,'clig'_off]", unreadCount > 0
36
- ? "font-[510] text-black"
37
- : "font-normal tracking-[0.14px] text-[#5C6366]"), children: latestMessagePreview }), unreadCount > 0 && _jsx(UnreadBadge, { count: unreadCount })] })] })] }));
29
+ return (_jsx(ThreadCard, { testId: "biz-inbox-thread-card", onClick: () => onClick(item), unreadCount: unreadCount, time: latestMessageTime, preview: latestMessagePreview, avatar: _jsx(BizThreadAvatar, { peerType: item.peer.type, chatCategory: item.chatCategory, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), title: _jsx(BizThreadTitle, { peerType: item.peer.type, chatCategory: item.chatCategory, displayName: displayName, hasUnread: unreadCount > 0 }) }));
38
30
  };
39
31
  export default BizThreadCard;
@@ -12,7 +12,7 @@ import useConversationStore from "../../store/conversation";
12
12
  import useAuthStore from "../../store/auth";
13
13
  import { isBizGroupChat } from "../../utils/bizConversation";
14
14
  import emitter from "../../utils/events";
15
- import BizMessageItem from "./item/BizMessageItem";
15
+ import MessageItem from "../../components/message/item";
16
16
  const BizMessageList = () => {
17
17
  const { t } = useTranslation("biz-inbox");
18
18
  const { user } = useChatContext();
@@ -31,6 +31,6 @@ const BizMessageList = () => {
31
31
  emitter.off("BIZ_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM", scrollToBottomIfAtBottom);
32
32
  };
33
33
  }, [scrollToBottomIfAtBottom]);
34
- return (_jsx("div", { "data-testid": "biz-message-list", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableBizMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: loadState.messageList.map((message, _, array) => (_jsx(BizMessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat }, message.clientMsgID))) })) }));
34
+ return (_jsx("div", { "data-testid": "biz-message-list", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableBizMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat }, message.clientMsgID))) })) }));
35
35
  };
36
36
  export default BizMessageList;
@@ -1 +1 @@
1
- {"version":3,"file":"BizThreadDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizThreadDetailInput.tsx"],"names":[],"mappings":"AA+BA,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,4CAiZ3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"BizThreadDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/biz-thread-detail/BizThreadDetailInput.tsx"],"names":[],"mappings":"AA+BA,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,4CA0Y3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -12,11 +12,11 @@ import useConversationStore from "../../store/conversation";
12
12
  import useBizMessageDraftStore from "../../store/bizMessageDraft";
13
13
  import { resolveBizAvatarUrl, resolveBizDisplayName, } from "../../utils/bizConversation";
14
14
  import { emit } from "../../utils/events";
15
- import { extractBizLinkUrls } from "../../utils/bizLink";
15
+ import { extractLinkUrls } from "../../utils/link";
16
16
  import { useFetchUrlMetadata } from "../../hooks/common/useFetchUrlMetadata";
17
17
  import { processAndValidateDocuments, processAndValidateFiles, } from "../../utils/fileValidation";
18
- import BizAttachmentPreview from "./BizAttachmentPreview";
19
- import BizLinkInputPreview from "./BizLinkInputPreview";
18
+ import { FilePreviewList } from "../../components/message/footer/FilePreview";
19
+ import LinkInputPreview from "../../components/message/footer/LinkInputPreview";
20
20
  import BizMoreOptionPanel from "./BizMoreOptionPanel";
21
21
  const MAX_LINES = 5;
22
22
  const CONTAINER_MIN_HEIGHT = 48;
@@ -29,7 +29,6 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
29
29
  const conversationId = selectedConversationId || (bizConversation === null || bizConversation === void 0 ? void 0 : bizConversation.conversationId) || "";
30
30
  const isConversationReady = !!conversationData && (!!conversationData.userID || !!conversationData.groupID);
31
31
  const sendMessageOptions = useMemo(() => ({
32
- extractUrls: extractBizLinkUrls,
33
32
  scrollEvent: "BIZ_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM",
34
33
  resolveOfflinePushMeta: () => ({
35
34
  title: bizConversation
@@ -43,7 +42,7 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
43
42
  "",
44
43
  }),
45
44
  }), [bizConversation, conversationData, t]);
46
- const { sendTextMessage, sendMergeMessage } = useSendMessage(sendMessageOptions);
45
+ const { sendMergeMessage } = useSendMessage(sendMessageOptions);
47
46
  const userID = useAuthStore((state) => state.userID);
48
47
  const setDraft = useBizMessageDraftStore((state) => state.setDraft);
49
48
  const clearDraft = useBizMessageDraftStore((state) => state.clearDraft);
@@ -59,9 +58,8 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
59
58
  const [previewDismissed, setPreviewDismissed] = useState(false);
60
59
  const [isMoreOptionOpen, { setFalse: closeMoreOption, toggle: toggleMoreOption },] = useBoolean(false);
61
60
  const previewUrl = useMemo(() => {
62
- var _a;
63
- const urls = extractBizLinkUrls(value);
64
- return (_a = urls[0]) !== null && _a !== void 0 ? _a : null;
61
+ const urls = extractLinkUrls(value);
62
+ return urls.length === 1 ? urls[0] : null;
65
63
  }, [value]);
66
64
  const showLinkPreview = !!previewUrl && !previewDismissed;
67
65
  const { data: previewMetadata } = useFetchUrlMetadata(previewUrl !== null && previewUrl !== void 0 ? previewUrl : "", showLinkPreview);
@@ -185,15 +183,6 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
185
183
  if (onSendMessage) {
186
184
  onSendMessage(text);
187
185
  }
188
- else if (hasFiles) {
189
- await sendMergeMessage({
190
- plainText: text,
191
- richText: "",
192
- files: listUploadFiles,
193
- isInternal: false,
194
- });
195
- setListUploadFiles([]);
196
- }
197
186
  else {
198
187
  const urlMetadata = showLinkPreview && previewUrl && previewMetadata
199
188
  ? {
@@ -203,14 +192,16 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
203
192
  image: previewMetadata.image,
204
193
  }
205
194
  : undefined;
206
- const sent = await sendTextMessage({
195
+ const sent = await sendMergeMessage({
207
196
  plainText: text,
208
197
  richText: "",
198
+ files: listUploadFiles,
209
199
  isInternal: false,
210
200
  urlMetadata,
211
201
  });
212
202
  if (!sent)
213
203
  return;
204
+ setListUploadFiles([]);
214
205
  }
215
206
  setValue("");
216
207
  if (conversationId && userID) {
@@ -258,7 +249,7 @@ const BizThreadDetailInput = ({ placeholder, onSendMessage, onInputChange, }) =>
258
249
  event.preventDefault();
259
250
  void handleSend();
260
251
  };
261
- 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
252
+ 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(FilePreviewList, { files: listUploadFiles, onRemove: (file) => handleRemoveFile(file.uid), onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "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
262
253
  ? "border-[#607CFB] shadow-[0px_0px_2px_rgba(27,63,228,0.5)]"
263
254
  : "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(); } }))] }));
264
255
  };
@@ -3,12 +3,8 @@ export type { BizThreadDetailHeaderProps } from "./BizThreadDetailHeader";
3
3
  export { default as BizThreadDetailInput } from "./BizThreadDetailInput";
4
4
  export type { BizThreadDetailInputProps } from "./BizThreadDetailInput";
5
5
  export { default as BizMessageList } from "./BizMessageList";
6
- export { default as BizMessageItem } from "./item/BizMessageItem";
7
- export type { BizMessageItemProps } from "./item/BizMessageItem";
8
6
  export { default as BizMoreOptionPanel } from "./BizMoreOptionPanel";
9
7
  export type { BizMoreOptionPanelProps } from "./BizMoreOptionPanel";
10
8
  export { default as BizMoreOptionItem } from "./BizMoreOptionItem";
11
9
  export type { BizMoreOptionItemProps } from "./BizMoreOptionItem";
12
- export { default as BizAttachmentPreview } from "./BizAttachmentPreview";
13
- export type { BizAttachmentPreviewProps } from "./BizAttachmentPreview";
14
10
  //# 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,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,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;AAE7D,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"}
@@ -1,7 +1,5 @@
1
1
  export { default as BizThreadDetailHeader } from "./BizThreadDetailHeader";
2
2
  export { default as BizThreadDetailInput } from "./BizThreadDetailInput";
3
3
  export { default as BizMessageList } from "./BizMessageList";
4
- export { default as BizMessageItem } from "./item/BizMessageItem";
5
4
  export { default as BizMoreOptionPanel } from "./BizMoreOptionPanel";
6
5
  export { default as BizMoreOptionItem } from "./BizMoreOptionItem";
7
- export { default as BizAttachmentPreview } from "./BizAttachmentPreview";
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQhD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDAyFhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAShD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDA8DhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -1,12 +1,13 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import useConversationStore from "../../store/conversation";
5
5
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
6
- import { Avatar, Badge } from "antd";
6
+ import { Avatar } from "antd";
7
7
  import { useChatContext } from "../../context/ChatContext";
8
8
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
9
9
  import { formatTimestamp, parseLatestMessage } from "../../utils/common";
10
+ import ThreadCard from "./ThreadCard";
10
11
  const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
11
12
  var _a;
12
13
  const { t } = useTranslation();
@@ -34,8 +35,8 @@ const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
34
35
  const latestMsgSendTime = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsgSendTime) || 0;
35
36
  if (!conversation)
36
37
  return null;
37
- return (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-100 cursor-pointer transition-colors ${isSelected ? "bg-blue-50" : "bg-white"}`, children: [isSelected && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "relative flex-shrink-0", children: _jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: displayName }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: parseLatestMessage(latestMsg, user === null || user === void 0 ? void 0 : user.userID, t) })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [latestMsgSendTime > 0 && (_jsx("span", { className: "text-xs text-gray-400", children: formatTimestamp(latestMsgSendTime, {
38
- hasTime: true,
39
- }) })), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.conversationID));
38
+ return (_jsx(ThreadCard, { onClick: () => handleConversationClick(conversation), isSelected: isSelected, unreadCount: conversation.unreadCount, preview: parseLatestMessage(latestMsg, user === null || user === void 0 ? void 0 : user.userID, t), time: latestMsgSendTime > 0
39
+ ? formatTimestamp(latestMsgSendTime, { hasTime: true })
40
+ : undefined, avatar: _jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), title: _jsx("h3", { className: "truncate text-sm font-semibold text-gray-900", children: displayName }) }));
40
41
  };
41
42
  export default ConversationBySessionItem;
@@ -0,0 +1,14 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ThreadCardProps {
3
+ avatar: ReactNode;
4
+ title: ReactNode;
5
+ preview: string;
6
+ time?: string;
7
+ unreadCount?: number;
8
+ isSelected?: boolean;
9
+ onClick: () => void;
10
+ testId?: string;
11
+ }
12
+ declare const ThreadCard: ({ avatar, title, preview, time, unreadCount, isSelected, onClick, testId, }: ThreadCardProps) => import("react/jsx-runtime").JSX.Element;
13
+ export default ThreadCard;
14
+ //# sourceMappingURL=ThreadCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThreadCard.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ThreadCard.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAoBvC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,QAAA,MAAM,UAAU,GAAI,6EASjB,eAAe,4CA4CjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ const formatUnreadCount = (count) => (count > 99 ? "99+" : count);
5
+ const UnreadBadge = ({ count }) => {
6
+ const label = String(formatUnreadCount(count));
7
+ const isWide = label.length > 1;
8
+ return (_jsx("span", { className: clsx("inline-flex h-5 shrink-0 items-center justify-center rounded-[32px] border-2 border-white bg-[linear-gradient(93deg,#FF7D74_0%,#F04438_100%)] px-[3px] text-center text-[12px] font-semibold leading-[160%] text-white", isWide ? "min-w-8" : "min-w-5"), children: label }));
9
+ };
10
+ const ThreadCard = ({ avatar, title, preview, time, unreadCount = 0, isSelected = false, onClick, testId, }) => {
11
+ const hasUnread = unreadCount > 0;
12
+ return (_jsxs("button", { type: "button", onClick: onClick, "data-testid": testId, className: clsx("relative flex w-full shrink-0 items-start gap-2 border-b border-[#EFEFEF] p-3 text-left transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]", isSelected ? "bg-blue-50" : "bg-white"), children: [isSelected && (_jsx("span", { className: "absolute bottom-0 left-0 top-0 w-1 bg-blue-500" })), avatar, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex min-h-[25.6px] items-center justify-between gap-1", children: [_jsx("div", { className: "min-w-0 flex-1", children: title }), time && (_jsx("span", { className: "shrink-0 text-[12px] font-normal leading-[160%] text-[#747B7E] [font-feature-settings:'liga'_off,'clig'_off]", children: time }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: clsx("min-w-0 flex-1 truncate text-[14px] leading-[160%] [font-feature-settings:'liga'_off,'clig'_off]", hasUnread
13
+ ? "font-[510] text-black"
14
+ : "font-normal tracking-[0.14px] text-[#5C6366]"), children: preview }), hasUnread && _jsx(UnreadBadge, { count: unreadCount })] })] })] }));
15
+ };
16
+ export default ThreadCard;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageCollection.d.ts","sourceRoot":"","sources":["../../../src/components/media-collection/ImageCollection.tsx"],"names":[],"mappings":"AAYA,QAAA,MAAM,eAAe,+CA+GpB,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ImageCollection.d.ts","sourceRoot":"","sources":["../../../src/components/media-collection/ImageCollection.tsx"],"names":[],"mappings":"AAYA,QAAA,MAAM,eAAe,+CAqFpB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -2,13 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MessageType } from "@openim/wasm-client-sdk";
3
3
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
4
4
  import InfiniteScroll from "react-infinite-scroll-component";
5
- import { Button, Empty, Image, Spin } from "antd";
5
+ import { Empty, Image, Spin } from "antd";
6
6
  import { useCallback } from "react";
7
7
  import dayjs from "dayjs";
8
8
  import useConversationStore from "../../store/conversation";
9
9
  import { images } from "../../constants/images";
10
10
  import { useTranslation } from "react-i18next";
11
- import { DownloadOutlined } from "@ant-design/icons";
11
+ import { renderImagePreviewToolbar } from "../../components/message/item/imagePreviewToolbar";
12
12
  const ImageCollection = () => {
13
13
  const { t } = useTranslation();
14
14
  const selectedSourceId = useConversationStore((state) => state.selectedSourceId);
@@ -18,16 +18,6 @@ const ImageCollection = () => {
18
18
  contentType: MessageType.PictureMessage,
19
19
  },
20
20
  });
21
- const handleDownload = (imageUrl) => {
22
- if (!imageUrl)
23
- return;
24
- const link = document.createElement("a");
25
- link.href = imageUrl;
26
- link.download = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
27
- document.body.appendChild(link);
28
- link.click();
29
- document.body.removeChild(link);
30
- };
31
21
  const renderItem = useCallback((date, items) => {
32
22
  return (_jsxs("div", { className: "mb-2", children: [_jsx("span", { className: "text-sm font-medium text-gray-500 px-3", children: dayjs(date).format("DD MMMM, YYYY") }), _jsx("div", { className: "grid grid-cols-3 justify-start gap-px py-2", children: items.map((item) => {
33
23
  var _a, _b, _c, _d;
@@ -42,11 +32,7 @@ const ImageCollection = () => {
42
32
  if (isLoading)
43
33
  return (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Spin, {}) }));
44
34
  return (_jsx("div", { id: "scrollableImageDiv", className: "h-full overflow-auto", children: _jsx(Image.PreviewGroup, { preview: {
45
- toolbarRender: (originalNode, info) => {
46
- var _a;
47
- const imageUrl = (_a = info === null || info === void 0 ? void 0 : info.image) === null || _a === void 0 ? void 0 : _a.url;
48
- return (_jsxs("div", { className: "flex flex-col justify-center gap-2", children: [originalNode, imageUrl && (_jsx(Button, { type: "primary", icon: _jsx(DownloadOutlined, {}), className: "self-center", onClick: () => handleDownload(imageUrl), children: t("download") }))] }));
49
- },
35
+ 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); },
50
36
  }, children: _jsx(InfiniteScroll, { dataLength: dataFlatten.length, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableImageDiv", children: Object.entries(groupedData).map(([date, items]) => renderItem(date, items)) }) }) }));
51
37
  };
52
38
  export default ImageCollection;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AA2BA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAqQ3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AA0BA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAgQ3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -11,7 +11,6 @@ import MessageInfiniteScroll from "./MessageInfiniteScroll";
11
11
  import MessageHeader from "./MessageHeader";
12
12
  import MessageFooter from "./footer";
13
13
  import UnassignedSessionFooter from "./UnassignedSessionFooter";
14
- import { images } from "../../constants/images";
15
14
  import { useTranslation } from "react-i18next";
16
15
  import useConversationStore from "../../store/conversation";
17
16
  import { useBoolean } from "ahooks";
@@ -130,11 +129,6 @@ const MessageList = (props) => {
130
129
  if (!conversationData) {
131
130
  return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
132
131
  }
133
- return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0", style: {
134
- backgroundImage: `url(${images.conversationBg})`,
135
- backgroundSize: "cover",
136
- backgroundPosition: "center",
137
- overflowX: "hidden",
138
- }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
132
+ return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
139
133
  };
140
134
  export default MessageList;
@@ -0,0 +1,6 @@
1
+ interface ComposerTextSyncPluginProps {
2
+ onChange: (text: string) => void;
3
+ }
4
+ declare const ComposerTextSyncPlugin: ({ onChange }: ComposerTextSyncPluginProps) => null;
5
+ export default ComposerTextSyncPlugin;
6
+ //# sourceMappingURL=ComposerTextSyncPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComposerTextSyncPlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ComposerTextSyncPlugin.tsx"],"names":[],"mappings":"AAMA,UAAU,2BAA2B;IACnC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,QAAA,MAAM,sBAAsB,GAAI,cAAc,2BAA2B,SAYxE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
+ import { $getRoot } from "lexical";
4
+ import { useEffect } from "react";
5
+ const ComposerTextSyncPlugin = ({ onChange }) => {
6
+ const [editor] = useLexicalComposerContext();
7
+ useEffect(() => {
8
+ return editor.registerUpdateListener(({ editorState }) => {
9
+ editorState.read(() => {
10
+ onChange($getRoot().getTextContent());
11
+ });
12
+ });
13
+ }, [editor, onChange]);
14
+ return null;
15
+ };
16
+ export default ComposerTextSyncPlugin;
@@ -4,10 +4,11 @@ interface ShortenOptions {
4
4
  keepStart?: number;
5
5
  keepEnd?: number;
6
6
  }
7
- export declare const shortenFileName: (name: string, options?: ShortenOptions) => string;
8
- export declare function FilePreviewList({ files, onRemove, }: {
7
+ export declare const shortenFileName: (name?: string, options?: ShortenOptions) => string;
8
+ export declare function FilePreviewList({ files, onRemove, onVideoReady, }: {
9
9
  files: UploadFile[];
10
10
  onRemove: (file: UploadFile) => void;
11
+ onVideoReady?: () => void;
11
12
  }): import("react/jsx-runtime").JSX.Element | null;
12
13
  declare const FilePreview: () => import("react/jsx-runtime").JSX.Element;
13
14
  export default FilePreview;
@@ -1 +1 @@
1
- {"version":3,"file":"FilePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/FilePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAS1C,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,UAAS,cAAmB,WAiBzE,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACtC,kDA8EA;AAED,QAAA,MAAM,WAAW,+CAQhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"FilePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/FilePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAOlC,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,UAAS,cAAmB,WAmB1E,CAAC;AAwIF,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,YAAY,GACb,EAAE;IACD,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,kDAsBA;AAED,QAAA,MAAM,WAAW,+CAQhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -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-[#393E40]", "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-[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, { 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-[#EAF5FF] 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-[#EAF5FF]", children: isLoading ? (_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-[#1B3FE4] 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-[#1B3FE4]" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [isLoading ? (_jsx("p", { className: "truncate text-[14px] font-medium leading-[1.6] text-[#393E40]", children: t("link_preview.loading") })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "truncate text-[14px] font-medium leading-[1.6] text-[#393E40]", 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-[#747B7E]", children: (data === null || data === void 0 ? void 0 : data.description) || getHostLabel(url) }))] })), isError && !isLoading && (_jsx("p", { className: "truncate text-[13px] leading-[1.6] text-[#747B7E]", 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-[#393E40]" }) })] }));
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"}