@droppii-org/chat-sdk 0.1.55 → 0.1.57
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.
- package/dist/components/biz-inbox/BizThreadAvatar.d.ts +3 -3
- package/dist/components/biz-inbox/BizThreadAvatar.d.ts.map +1 -1
- package/dist/components/biz-inbox/BizThreadAvatar.js +5 -5
- package/dist/components/biz-inbox/BizThreadCard.d.ts +3 -3
- package/dist/components/biz-inbox/BizThreadCard.d.ts.map +1 -1
- package/dist/components/biz-inbox/BizThreadCard.js +7 -8
- package/dist/components/biz-inbox/BizThreadList.d.ts +3 -3
- package/dist/components/biz-inbox/BizThreadList.d.ts.map +1 -1
- package/dist/components/biz-inbox/BizThreadList.js +4 -8
- package/dist/components/biz-inbox/BizThreadTitle.d.ts +3 -3
- package/dist/components/biz-inbox/BizThreadTitle.d.ts.map +1 -1
- package/dist/components/biz-inbox/BizThreadTitle.js +5 -5
- package/dist/components/biz-thread-detail/BizMessageList.d.ts.map +1 -1
- package/dist/components/biz-thread-detail/BizMessageList.js +4 -5
- package/dist/components/biz-thread-detail/BizThreadDetailHeader.d.ts +2 -2
- package/dist/components/biz-thread-detail/BizThreadDetailHeader.d.ts.map +1 -1
- package/dist/components/biz-thread-detail/BizThreadDetailHeader.js +7 -5
- package/dist/components/biz-thread-detail/BizThreadDetailInput.d.ts.map +1 -1
- package/dist/components/biz-thread-detail/BizThreadDetailInput.js +8 -13
- package/dist/components/biz-thread-detail/bot/BotButtonMessage.d.ts +1 -1
- package/dist/components/biz-thread-detail/bot/BotButtonMessage.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationDetailHeader.d.ts +8 -0
- package/dist/components/conversation-detail/ConversationDetailHeader.d.ts.map +1 -0
- package/dist/components/conversation-detail/ConversationDetailHeader.js +18 -0
- package/dist/components/conversation-detail/ConversationDetailInput.d.ts +8 -0
- package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -0
- package/dist/components/conversation-detail/ConversationDetailInput.js +258 -0
- package/dist/components/conversation-detail/ConversationMessageList.d.ts +7 -0
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -0
- package/dist/components/conversation-detail/ConversationMessageList.js +42 -0
- package/dist/components/conversation-detail/ConversationMoreOptionItem.d.ts +8 -0
- package/dist/components/conversation-detail/ConversationMoreOptionItem.d.ts.map +1 -0
- package/dist/components/conversation-detail/ConversationMoreOptionItem.js +19 -0
- package/dist/components/conversation-detail/ConversationMoreOptionPanel.d.ts +10 -0
- package/dist/components/conversation-detail/ConversationMoreOptionPanel.d.ts.map +1 -0
- package/dist/components/conversation-detail/ConversationMoreOptionPanel.js +20 -0
- package/dist/components/conversation-detail/bot/BotButtonMessage.d.ts +10 -0
- package/dist/components/conversation-detail/bot/BotButtonMessage.d.ts.map +1 -0
- package/dist/components/conversation-detail/bot/BotButtonMessage.js +30 -0
- package/dist/components/conversation-detail/bot/BotStartButton.d.ts +8 -0
- package/dist/components/conversation-detail/bot/BotStartButton.d.ts.map +1 -0
- package/dist/components/conversation-detail/bot/BotStartButton.js +11 -0
- package/dist/components/conversation-detail/bot/BotSuggest.d.ts +7 -0
- package/dist/components/conversation-detail/bot/BotSuggest.d.ts.map +1 -0
- package/dist/components/conversation-detail/bot/BotSuggest.js +11 -0
- package/dist/components/conversation-detail/bot/index.d.ts +7 -0
- package/dist/components/conversation-detail/bot/index.d.ts.map +1 -0
- package/dist/components/conversation-detail/bot/index.js +3 -0
- package/dist/components/conversation-detail/index.d.ts +10 -0
- package/dist/components/conversation-detail/index.d.ts.map +1 -0
- package/dist/components/conversation-detail/index.js +5 -0
- package/dist/components/conversation-inbox/ConversationInboxHeader.d.ts +3 -0
- package/dist/components/conversation-inbox/ConversationInboxHeader.d.ts.map +1 -0
- package/dist/components/conversation-inbox/ConversationInboxHeader.js +8 -0
- package/dist/components/conversation-inbox/ConversationInboxItem.d.ts +8 -0
- package/dist/components/conversation-inbox/ConversationInboxItem.d.ts.map +1 -0
- package/dist/components/conversation-inbox/ConversationInboxItem.js +30 -0
- package/dist/components/conversation-inbox/ConversationInboxList.d.ts +12 -0
- package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -0
- package/dist/components/conversation-inbox/ConversationInboxList.js +36 -0
- package/dist/components/conversation-inbox/ConversationInboxTitle.d.ts +9 -0
- package/dist/components/conversation-inbox/ConversationInboxTitle.d.ts.map +1 -0
- package/dist/components/conversation-inbox/ConversationInboxTitle.js +7 -0
- package/dist/components/conversation-inbox/ConversationPeerAvatar.d.ts +10 -0
- package/dist/components/conversation-inbox/ConversationPeerAvatar.d.ts.map +1 -0
- package/dist/components/conversation-inbox/ConversationPeerAvatar.js +10 -0
- package/dist/components/conversation-inbox/index.d.ts +10 -0
- package/dist/components/conversation-inbox/index.d.ts.map +1 -0
- package/dist/components/conversation-inbox/index.js +5 -0
- package/dist/components/message/footer/LinkInputPreview.js +1 -1
- package/dist/components/message/item/ImageMessage.js +1 -1
- package/dist/components/message/item/LinkCard.js +1 -1
- package/dist/components/message/item/LinkText.js +1 -1
- package/dist/components/message/item/VideoMessage.js +1 -1
- package/dist/components/message/item/index.d.ts +2 -1
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +51 -38
- package/dist/components/message/item/mediaSize.d.ts.map +1 -1
- package/dist/components/message/item/mediaSize.js +8 -8
- package/dist/components/message/toolbox/MessageToolboxMenu.d.ts +5 -0
- package/dist/components/message/toolbox/MessageToolboxMenu.d.ts.map +1 -0
- package/dist/components/message/toolbox/MessageToolboxMenu.js +10 -0
- package/dist/components/message/toolbox/getCopyableMessageText.d.ts +4 -0
- package/dist/components/message/toolbox/getCopyableMessageText.d.ts.map +1 -0
- package/dist/components/message/toolbox/getCopyableMessageText.js +28 -0
- package/dist/components/message/toolbox/index.d.ts +5 -0
- package/dist/components/message/toolbox/index.d.ts.map +1 -0
- package/dist/components/message/toolbox/index.js +4 -0
- package/dist/components/message/toolbox/types.d.ts +15 -0
- package/dist/components/message/toolbox/types.d.ts.map +1 -0
- package/dist/components/message/toolbox/types.js +6 -0
- package/dist/components/message/toolbox/useMessageToolboxVisibility.d.ts +9 -0
- package/dist/components/message/toolbox/useMessageToolboxVisibility.d.ts.map +1 -0
- package/dist/components/message/toolbox/useMessageToolboxVisibility.js +15 -0
- package/dist/constants/moreOption.d.ts +9 -0
- package/dist/constants/moreOption.d.ts.map +1 -0
- package/dist/constants/moreOption.js +37 -0
- package/dist/hooks/conversation/useActiveConversation.d.ts +3 -0
- package/dist/hooks/conversation/useActiveConversation.d.ts.map +1 -0
- package/dist/hooks/conversation/useActiveConversation.js +31 -0
- package/dist/hooks/conversation/useBotConversationDetail.d.ts +3 -0
- package/dist/hooks/conversation/useBotConversationDetail.d.ts.map +1 -0
- package/dist/hooks/conversation/useBotConversationDetail.js +18 -0
- package/dist/hooks/conversation/useBotFlow.d.ts +18 -0
- package/dist/hooks/conversation/useBotFlow.d.ts.map +1 -0
- package/dist/hooks/conversation/useBotFlow.js +150 -0
- package/dist/hooks/conversation/useConversationList.d.ts +15 -0
- package/dist/hooks/conversation/useConversationList.d.ts.map +1 -0
- package/dist/hooks/conversation/useConversationList.js +70 -0
- package/dist/hooks/conversation/useCreateConversation.d.ts +8 -0
- package/dist/hooks/conversation/useCreateConversation.d.ts.map +1 -0
- package/dist/hooks/conversation/useCreateConversation.js +15 -0
- package/dist/hooks/conversation/useResetBotFlow.d.ts +3 -0
- package/dist/hooks/conversation/useResetBotFlow.d.ts.map +1 -0
- package/dist/hooks/conversation/useResetBotFlow.js +14 -0
- package/dist/hooks/conversation/useRunBotFlow.d.ts +7 -0
- package/dist/hooks/conversation/useRunBotFlow.d.ts.map +1 -0
- package/dist/hooks/conversation/useRunBotFlow.js +14 -0
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +15 -7
- package/dist/hooks/message/useSendMessage.d.ts +1 -1
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/user/useAuth.js +2 -2
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/locales/i18n.js +3 -3
- package/dist/locales/i18n.ts +3 -3
- package/dist/locales/vi/biz-inbox.json +39 -39
- package/dist/locales/vi/common.json +3 -0
- package/dist/locales/vi/conversation-inbox.json +39 -0
- package/dist/screens/biz-message/index.d.ts +3 -4
- package/dist/screens/biz-message/index.d.ts.map +1 -1
- package/dist/screens/biz-message/index.js +5 -9
- package/dist/screens/biz-thread-detail/index.js +6 -6
- package/dist/screens/conversation-detail/index.d.ts +9 -0
- package/dist/screens/conversation-detail/index.d.ts.map +1 -0
- package/dist/screens/conversation-detail/index.js +53 -0
- package/dist/screens/conversation-inbox/index.d.ts +8 -0
- package/dist/screens/conversation-inbox/index.d.ts.map +1 -0
- package/dist/screens/conversation-inbox/index.js +30 -0
- package/dist/services/query.d.ts +0 -1
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +0 -1
- package/dist/services/routes.d.ts +0 -1
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +0 -1
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +14 -5
- package/dist/store/messageDraft.d.ts +9 -0
- package/dist/store/messageDraft.d.ts.map +1 -0
- package/dist/store/messageDraft.js +35 -0
- package/dist/store/type.d.ts +6 -1
- package/dist/store/type.d.ts.map +1 -1
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/conversation.d.ts +116 -0
- package/dist/types/conversation.d.ts.map +1 -0
- package/dist/types/conversation.js +33 -0
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +5 -3
- package/dist/utils/conversation.d.ts +12 -0
- package/dist/utils/conversation.d.ts.map +1 -0
- package/dist/utils/conversation.js +53 -0
- package/dist/utils/events.d.ts +1 -1
- package/dist/utils/events.d.ts.map +1 -1
- package/dist/utils/fileValidation.d.ts +2 -1
- package/dist/utils/fileValidation.d.ts.map +1 -1
- package/dist/utils/fileValidation.js +10 -1
- package/dist/utils/imCommon.d.ts +1 -0
- package/dist/utils/imCommon.d.ts.map +1 -1
- package/package.json +126 -127
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { useBoolean } from "ahooks";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { Icon } from "../../components/icon";
|
|
8
|
+
import { useSendMessage } from "../../hooks/message/useSendMessage";
|
|
9
|
+
import useAuthStore from "../../store/auth";
|
|
10
|
+
import useConversationStore from "../../store/conversation";
|
|
11
|
+
import useMessageDraftStore from "../../store/messageDraft";
|
|
12
|
+
import { resolveConversationAvatarUrl, resolveConversationDisplayName, } from "../../utils/conversation";
|
|
13
|
+
import { emit } from "../../utils/events";
|
|
14
|
+
import { extractLinkUrls } from "../../utils/link";
|
|
15
|
+
import { useFetchUrlMetadata } from "../../hooks/common/useFetchUrlMetadata";
|
|
16
|
+
import { processAndValidateDocuments, processAndValidateFiles, } from "../../utils/fileValidation";
|
|
17
|
+
import { FilePreviewList } from "../../components/message/footer/FilePreview";
|
|
18
|
+
import LinkInputPreview from "../../components/message/footer/LinkInputPreview";
|
|
19
|
+
import ConversationMoreOptionPanel from "./ConversationMoreOptionPanel";
|
|
20
|
+
const MAX_LINES = 5;
|
|
21
|
+
const CONTAINER_MIN_HEIGHT = 48;
|
|
22
|
+
const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, }) => {
|
|
23
|
+
const { t } = useTranslation("conversation-inbox");
|
|
24
|
+
const { t: tCommon } = useTranslation("common");
|
|
25
|
+
const conversationData = useConversationStore((state) => state.conversationData);
|
|
26
|
+
const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
|
|
27
|
+
const conversationId = selectedConversationId || (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
|
|
28
|
+
const isConversationReady = !!conversationData && (!!conversationData.userID || !!conversationData.groupID);
|
|
29
|
+
const sendMessageOptions = useMemo(() => ({
|
|
30
|
+
scrollEvent: "CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM",
|
|
31
|
+
resolveOfflinePushMeta: () => ({
|
|
32
|
+
title: conversationData
|
|
33
|
+
? resolveConversationDisplayName(conversationData, t)
|
|
34
|
+
: "Droppii Chat",
|
|
35
|
+
icon: (conversationData && resolveConversationAvatarUrl(conversationData)) ||
|
|
36
|
+
"",
|
|
37
|
+
conversationId: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "",
|
|
38
|
+
}),
|
|
39
|
+
}), [conversationData, t]);
|
|
40
|
+
const { sendMergeMessage } = useSendMessage(sendMessageOptions);
|
|
41
|
+
const userID = useAuthStore((state) => state.userID);
|
|
42
|
+
const setDraft = useMessageDraftStore((state) => state.setDraft);
|
|
43
|
+
const clearDraft = useMessageDraftStore((state) => state.clearDraft);
|
|
44
|
+
const containerRef = useRef(null);
|
|
45
|
+
const textareaRef = useRef(null);
|
|
46
|
+
const mediaInputRef = useRef(null);
|
|
47
|
+
const documentInputRef = useRef(null);
|
|
48
|
+
const [value, setValue] = useState("");
|
|
49
|
+
const [listUploadFiles, setListUploadFiles] = useState([]);
|
|
50
|
+
const [isVideoLoading, setIsVideoLoading] = useState(false);
|
|
51
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
52
|
+
const [isSending, setIsSending] = useState(false);
|
|
53
|
+
const [previewDismissed, setPreviewDismissed] = useState(false);
|
|
54
|
+
const [isMoreOptionOpen, { setFalse: closeMoreOption, toggle: toggleMoreOption },] = useBoolean(false);
|
|
55
|
+
const previewUrl = useMemo(() => {
|
|
56
|
+
const urls = extractLinkUrls(value);
|
|
57
|
+
return urls.length === 1 ? urls[0] : null;
|
|
58
|
+
}, [value]);
|
|
59
|
+
const showLinkPreview = !!previewUrl && !previewDismissed;
|
|
60
|
+
const { data: previewMetadata } = useFetchUrlMetadata(previewUrl !== null && previewUrl !== void 0 ? previewUrl : "", showLinkPreview);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setPreviewDismissed(false);
|
|
63
|
+
}, [previewUrl]);
|
|
64
|
+
const hasFiles = listUploadFiles.length > 0;
|
|
65
|
+
const canSend = (value.trim().length > 0 || hasFiles) &&
|
|
66
|
+
!isSending &&
|
|
67
|
+
isConversationReady &&
|
|
68
|
+
!isVideoLoading;
|
|
69
|
+
const getTextareaHeights = useCallback(() => {
|
|
70
|
+
const textarea = textareaRef.current;
|
|
71
|
+
if (!textarea)
|
|
72
|
+
return null;
|
|
73
|
+
const lineHeight = parseFloat(window.getComputedStyle(textarea).lineHeight);
|
|
74
|
+
return {
|
|
75
|
+
lineHeight,
|
|
76
|
+
maxHeight: lineHeight * MAX_LINES,
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
79
|
+
const adjustTextareaHeight = useCallback(() => {
|
|
80
|
+
const textarea = textareaRef.current;
|
|
81
|
+
const heights = getTextareaHeights();
|
|
82
|
+
if (!textarea || !heights)
|
|
83
|
+
return;
|
|
84
|
+
textarea.style.height = `${heights.lineHeight}px`;
|
|
85
|
+
const scrollHeight = textarea.scrollHeight;
|
|
86
|
+
const nextHeight = Math.min(Math.max(scrollHeight, heights.lineHeight), heights.maxHeight);
|
|
87
|
+
textarea.style.height = `${nextHeight}px`;
|
|
88
|
+
textarea.style.overflowY =
|
|
89
|
+
scrollHeight > heights.maxHeight ? "auto" : "hidden";
|
|
90
|
+
}, [getTextareaHeights]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
closeMoreOption();
|
|
93
|
+
setListUploadFiles([]);
|
|
94
|
+
setIsVideoLoading(false);
|
|
95
|
+
}, [conversationId, userID, closeMoreOption]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
var _a;
|
|
98
|
+
if (!conversationId || !userID) {
|
|
99
|
+
setValue("");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const draft = (_a = useMessageDraftStore.getState().drafts[`${userID}:${conversationId}`]) !== null && _a !== void 0 ? _a : "";
|
|
103
|
+
setValue(draft);
|
|
104
|
+
requestAnimationFrame(adjustTextareaHeight);
|
|
105
|
+
}, [conversationId, userID, adjustTextareaHeight]);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
adjustTextareaHeight();
|
|
108
|
+
}, [value, adjustTextareaHeight]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (!isFocused)
|
|
111
|
+
return;
|
|
112
|
+
const handlePointerDown = (event) => {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
if ((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))
|
|
115
|
+
return;
|
|
116
|
+
(_b = textareaRef.current) === null || _b === void 0 ? void 0 : _b.blur();
|
|
117
|
+
};
|
|
118
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
119
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
120
|
+
}, [isFocused]);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const viewport = window.visualViewport;
|
|
123
|
+
if (!viewport)
|
|
124
|
+
return;
|
|
125
|
+
const handleViewportChange = () => {
|
|
126
|
+
if (document.activeElement === textareaRef.current) {
|
|
127
|
+
emit("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM");
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
viewport.addEventListener("resize", handleViewportChange);
|
|
131
|
+
return () => viewport.removeEventListener("resize", handleViewportChange);
|
|
132
|
+
}, []);
|
|
133
|
+
const handleMediaInputChange = useCallback((event) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const files = Array.from((_a = event.target.files) !== null && _a !== void 0 ? _a : []);
|
|
136
|
+
if (files.length === 0)
|
|
137
|
+
return;
|
|
138
|
+
const validFiles = processAndValidateFiles(files, {
|
|
139
|
+
t: tCommon,
|
|
140
|
+
currentUploadedFiles: listUploadFiles,
|
|
141
|
+
});
|
|
142
|
+
if (validFiles.length > 0) {
|
|
143
|
+
setListUploadFiles((prev) => [...prev, ...validFiles]);
|
|
144
|
+
const hasVideo = validFiles.some((f) => { var _a; return (_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("video/"); });
|
|
145
|
+
if (hasVideo)
|
|
146
|
+
setIsVideoLoading(true);
|
|
147
|
+
}
|
|
148
|
+
event.target.value = "";
|
|
149
|
+
}, [tCommon, listUploadFiles]);
|
|
150
|
+
const handleDocumentInputChange = useCallback((event) => {
|
|
151
|
+
var _a;
|
|
152
|
+
const files = Array.from((_a = event.target.files) !== null && _a !== void 0 ? _a : []);
|
|
153
|
+
if (files.length === 0)
|
|
154
|
+
return;
|
|
155
|
+
const validFiles = processAndValidateDocuments(files, tCommon);
|
|
156
|
+
if (validFiles.length > 0) {
|
|
157
|
+
setListUploadFiles((prev) => [...prev, ...validFiles]);
|
|
158
|
+
}
|
|
159
|
+
event.target.value = "";
|
|
160
|
+
}, [tCommon]);
|
|
161
|
+
const handleRemoveFile = useCallback((uid) => {
|
|
162
|
+
setListUploadFiles((prev) => prev.filter((f) => f.uid !== uid));
|
|
163
|
+
setIsVideoLoading(false);
|
|
164
|
+
}, []);
|
|
165
|
+
const handleChange = (nextValue) => {
|
|
166
|
+
setValue(nextValue);
|
|
167
|
+
if (conversationId && userID) {
|
|
168
|
+
setDraft(userID, conversationId, nextValue);
|
|
169
|
+
}
|
|
170
|
+
onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(nextValue);
|
|
171
|
+
};
|
|
172
|
+
const resetComposer = () => {
|
|
173
|
+
setValue("");
|
|
174
|
+
setListUploadFiles([]);
|
|
175
|
+
setIsVideoLoading(false);
|
|
176
|
+
setPreviewDismissed(false);
|
|
177
|
+
if (conversationId && userID) {
|
|
178
|
+
clearDraft(userID, conversationId);
|
|
179
|
+
}
|
|
180
|
+
requestAnimationFrame(adjustTextareaHeight);
|
|
181
|
+
};
|
|
182
|
+
const handleSend = async () => {
|
|
183
|
+
const text = value.trim();
|
|
184
|
+
if ((!text && !hasFiles) || isSending)
|
|
185
|
+
return;
|
|
186
|
+
const filesToSend = listUploadFiles;
|
|
187
|
+
const urlMetadata = showLinkPreview && previewUrl && previewMetadata
|
|
188
|
+
? {
|
|
189
|
+
url: previewUrl,
|
|
190
|
+
title: previewMetadata.title,
|
|
191
|
+
description: previewMetadata.description,
|
|
192
|
+
image: previewMetadata.image,
|
|
193
|
+
}
|
|
194
|
+
: undefined;
|
|
195
|
+
resetComposer();
|
|
196
|
+
setIsSending(true);
|
|
197
|
+
try {
|
|
198
|
+
if (onSendMessage) {
|
|
199
|
+
onSendMessage(text);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
await sendMergeMessage({
|
|
203
|
+
plainText: text,
|
|
204
|
+
richText: "",
|
|
205
|
+
files: filesToSend,
|
|
206
|
+
isInternal: false,
|
|
207
|
+
urlMetadata,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error("handleSend", error);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
setIsSending(false);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const handleFocus = () => {
|
|
219
|
+
setIsFocused(true);
|
|
220
|
+
closeMoreOption();
|
|
221
|
+
emit("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM");
|
|
222
|
+
};
|
|
223
|
+
const handleBlur = () => {
|
|
224
|
+
setIsFocused(false);
|
|
225
|
+
};
|
|
226
|
+
const handleToggleMoreOption = () => {
|
|
227
|
+
var _a;
|
|
228
|
+
if (!isMoreOptionOpen) {
|
|
229
|
+
(_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
230
|
+
}
|
|
231
|
+
toggleMoreOption();
|
|
232
|
+
};
|
|
233
|
+
const insertNewline = (textarea) => {
|
|
234
|
+
const { selectionStart, selectionEnd } = textarea;
|
|
235
|
+
const nextValue = value.slice(0, selectionStart) + "\n" + value.slice(selectionEnd);
|
|
236
|
+
handleChange(nextValue);
|
|
237
|
+
requestAnimationFrame(() => {
|
|
238
|
+
textarea.selectionStart = selectionStart + 1;
|
|
239
|
+
textarea.selectionEnd = selectionStart + 1;
|
|
240
|
+
adjustTextareaHeight();
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
const handleKeyDown = (event) => {
|
|
244
|
+
if (event.key !== "Enter")
|
|
245
|
+
return;
|
|
246
|
+
if (event.shiftKey) {
|
|
247
|
+
event.preventDefault();
|
|
248
|
+
insertNewline(event.currentTarget);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
event.preventDefault();
|
|
252
|
+
void handleSend();
|
|
253
|
+
};
|
|
254
|
+
return (_jsxs("div", { ref: containerRef, "data-testid": "conversation-detail-input", className: "flex w-full shrink-0 flex-col bg-chat-bubble-neutral/[0.05]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), _jsx("input", { ref: documentInputRef, type: "file", accept: ".pdf,.doc,.docx", multiple: true, className: "hidden", onChange: handleDocumentInputChange }), listUploadFiles.length > 0 && (_jsx(FilePreviewList, { files: listUploadFiles, onRemove: (file) => handleRemoveFile(file.uid), onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "conversation-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-o", size: 24, className: "text-primary-400" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-gray-900" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
|
|
255
|
+
? "border-primary-200 shadow-primary-focus"
|
|
256
|
+
: "border-gray-50"), style: { minHeight: CONTAINER_MIN_HEIGHT }, children: _jsx("div", { className: "flex min-w-0 flex-[1_0_0] items-center gap-2 self-stretch px-3 py-1", children: _jsx("textarea", { ref: textareaRef, value: value, rows: 1, onChange: (event) => handleChange(event.target.value), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "conversation-detail-message-input", className: "conversation-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-gray-400 focus:border-0 focus:outline-none focus:ring-0" }) }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "conversation-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-chat-send" : "bg-chat-send opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(ConversationMoreOptionPanel, { disabled: !isConversationReady, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, onTriggerDocumentInput: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
|
|
257
|
+
};
|
|
258
|
+
export default ConversationDetailInput;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface ConversationMessageListProps {
|
|
3
|
+
bottomSlot?: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
declare const ConversationMessageList: ({ bottomSlot }: ConversationMessageListProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ConversationMessageList;
|
|
7
|
+
//# sourceMappingURL=ConversationMessageList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAczE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,gBAAgB,4BAA4B,4CA2F5E,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { Empty, Spin } from "antd";
|
|
5
|
+
import { useEventListener } from "ahooks";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
8
|
+
import MessageInfiniteScroll from "../../components/message/MessageInfiniteScroll";
|
|
9
|
+
import { useMessage } from "../../hooks/message/useMessage";
|
|
10
|
+
import { useMessageListScroll } from "../../hooks/message/useMessageListScroll";
|
|
11
|
+
import useConversationStore from "../../store/conversation";
|
|
12
|
+
import useAuthStore from "../../store/auth";
|
|
13
|
+
import { isGroupChat as resolveIsGroupChat } from "../../utils/conversation";
|
|
14
|
+
import emitter from "../../utils/events";
|
|
15
|
+
import MessageItem from "../../components/message/item";
|
|
16
|
+
const ConversationMessageList = ({ bottomSlot }) => {
|
|
17
|
+
const { t } = useTranslation("conversation-inbox");
|
|
18
|
+
const { user } = useChatContext();
|
|
19
|
+
const authUserID = useAuthStore((state) => state.userID);
|
|
20
|
+
const conversation = useConversationStore((state) => state.conversationData);
|
|
21
|
+
const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
|
|
22
|
+
const conversationId = selectedConversationId || (conversation === null || conversation === void 0 ? void 0 : conversation.conversationID) || "";
|
|
23
|
+
const isGroupChat = conversation ? resolveIsGroupChat(conversation) : false;
|
|
24
|
+
const messageController = useMessage(conversationId);
|
|
25
|
+
const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottomIfAtBottom, } = useMessageListScroll(Object.assign({ conversationId, userId: (user === null || user === void 0 ? void 0 : user.userID) || authUserID }, messageController));
|
|
26
|
+
const { loadState, moreOldLoading } = messageController;
|
|
27
|
+
const isMessageLoading = !conversationId || loadState.initLoading;
|
|
28
|
+
const [openToolboxMessageId, setOpenToolboxMessageId] = useState(null);
|
|
29
|
+
const closeToolbox = useCallback(() => setOpenToolboxMessageId(null), []);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
emitter.on("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM", scrollToBottomIfAtBottom);
|
|
32
|
+
return () => {
|
|
33
|
+
emitter.off("CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM", scrollToBottomIfAtBottom);
|
|
34
|
+
};
|
|
35
|
+
}, [scrollToBottomIfAtBottom]);
|
|
36
|
+
useEventListener("scroll", closeToolbox, {
|
|
37
|
+
target: scrollRef,
|
|
38
|
+
enable: !!openToolboxMessageId,
|
|
39
|
+
});
|
|
40
|
+
return (_jsx("div", { "data-testid": "conversation-message-list", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [bottomSlot, loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: closeToolbox, onRevokeMessage: closeToolbox, enableLongPress: true }, message.clientMsgID)))] })) }));
|
|
41
|
+
};
|
|
42
|
+
export default ConversationMessageList;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MoreOptionConfig } from "../../constants/moreOption";
|
|
2
|
+
export interface ConversationMoreOptionItemProps {
|
|
3
|
+
option: MoreOptionConfig;
|
|
4
|
+
onSelect: (option: MoreOptionConfig) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const ConversationMoreOptionItem: ({ option, onSelect }: ConversationMoreOptionItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default ConversationMoreOptionItem;
|
|
8
|
+
//# sourceMappingURL=ConversationMoreOptionItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationMoreOptionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMoreOptionItem.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC9C;AAED,QAAA,MAAM,0BAA0B,GAAI,sBAAsB,+BAA+B,4CA+BxF,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Icon } from "../../components/icon";
|
|
6
|
+
const ICON_SIZE = 24;
|
|
7
|
+
const ConversationMoreOptionItem = ({ option, onSelect }) => {
|
|
8
|
+
const { t } = useTranslation("conversation-inbox");
|
|
9
|
+
const label = t(option.labelKey);
|
|
10
|
+
const handleClick = () => {
|
|
11
|
+
if (!option.enabled)
|
|
12
|
+
return;
|
|
13
|
+
onSelect(option);
|
|
14
|
+
};
|
|
15
|
+
return (_jsxs("button", { type: "button", onClick: handleClick, disabled: !option.enabled, "aria-label": label, "data-testid": `biz-more-option-${option.labelKey}`, className: clsx("flex flex-col items-center gap-1 rounded-xl px-1 transition-colors", option.enabled
|
|
16
|
+
? "active:bg-chat-bubble-neutral/[0.08]"
|
|
17
|
+
: "cursor-not-allowed opacity-40"), children: [_jsx("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-gray-200/10", children: _jsx(Icon, { icon: option.icon, size: ICON_SIZE, className: "text-gray-900" }) }), _jsx("span", { className: "line-clamp-2 text-center text-[12px] leading-[1.4] text-gray-900", children: label })] }));
|
|
18
|
+
};
|
|
19
|
+
export default ConversationMoreOptionItem;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MoreOptionConfig } from "../../constants/moreOption";
|
|
2
|
+
export interface ConversationMoreOptionPanelProps {
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
onSelectOption?: (option: MoreOptionConfig) => void;
|
|
5
|
+
onTriggerMediaInput?: () => void;
|
|
6
|
+
onTriggerDocumentInput?: () => void;
|
|
7
|
+
}
|
|
8
|
+
declare const ConversationMoreOptionPanel: ({ disabled, onSelectOption, onTriggerMediaInput, onTriggerDocumentInput, }: ConversationMoreOptionPanelProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default ConversationMoreOptionPanel;
|
|
10
|
+
//# sourceMappingURL=ConversationMoreOptionPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationMoreOptionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMoreOptionPanel.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,2BAA2B,GAAI,4EAKlC,gCAAgC,4CA8BlC,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { MORE_OPTIONS, } from "../../constants/moreOption";
|
|
5
|
+
import ConversationMoreOptionItem from "./ConversationMoreOptionItem";
|
|
6
|
+
const ConversationMoreOptionPanel = ({ disabled = false, onSelectOption, onTriggerMediaInput, onTriggerDocumentInput, }) => {
|
|
7
|
+
const handleSelectOption = useCallback((option) => {
|
|
8
|
+
if (option.labelKey === "thread_detail.more_option.media") {
|
|
9
|
+
onTriggerMediaInput === null || onTriggerMediaInput === void 0 ? void 0 : onTriggerMediaInput();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (option.labelKey === "thread_detail.more_option.document") {
|
|
13
|
+
onTriggerDocumentInput === null || onTriggerDocumentInput === void 0 ? void 0 : onTriggerDocumentInput();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
onSelectOption === null || onSelectOption === void 0 ? void 0 : onSelectOption(option);
|
|
17
|
+
}, [onSelectOption, onTriggerMediaInput, onTriggerDocumentInput]);
|
|
18
|
+
return (_jsx("div", { "data-testid": "conversation-more-option-panel", className: "grid grid-cols-4 gap-y-5 bg-white py-5", children: MORE_OPTIONS.map((option) => (_jsx(ConversationMoreOptionItem, { option: Object.assign(Object.assign({}, option), { enabled: option.enabled && !disabled }), onSelect: handleSelectOption }, option.labelKey))) }));
|
|
19
|
+
};
|
|
20
|
+
export default ConversationMoreOptionPanel;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BotButtonOption } from "../../../types/conversation";
|
|
2
|
+
export interface BotButtonMessageProps {
|
|
3
|
+
buttons: BotButtonOption[];
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
onSelect: (actionId: string) => void;
|
|
6
|
+
onRestart: () => void;
|
|
7
|
+
}
|
|
8
|
+
declare const BotButtonMessage: ({ buttons, disabled, onSelect, onRestart, }: BotButtonMessageProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
export default BotButtonMessage;
|
|
10
|
+
//# sourceMappingURL=BotButtonMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotButtonMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotButtonMessage.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,QAAA,MAAM,gBAAgB,GAAI,6CAKvB,qBAAqB,mDA4EvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Icon } from "../../../components/icon";
|
|
6
|
+
import MessageBubble from "../../../components/message/item/MessageBubble";
|
|
7
|
+
import { BOT_BUTTON_MENU_THRESHOLD, BOT_DEFAULT_BUTTON_ID, } from "../../../constants/botFlow";
|
|
8
|
+
const CHEVRON_SIZE = 20;
|
|
9
|
+
const BotButtonMessage = ({ buttons, disabled, onSelect, onRestart, }) => {
|
|
10
|
+
const { t } = useTranslation("conversation-inbox");
|
|
11
|
+
if (buttons.length === 0)
|
|
12
|
+
return null;
|
|
13
|
+
const isMenuStyle = buttons.length > BOT_BUTTON_MENU_THRESHOLD;
|
|
14
|
+
const handleSelect = (actionId) => {
|
|
15
|
+
if (disabled)
|
|
16
|
+
return;
|
|
17
|
+
if (actionId === BOT_DEFAULT_BUTTON_ID) {
|
|
18
|
+
onRestart();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
onSelect(actionId);
|
|
22
|
+
};
|
|
23
|
+
const optionsNode = isMenuStyle ? (_jsx("div", { className: "flex flex-col overflow-hidden rounded-xl bg-white", children: buttons.map((option, index) => (_jsxs("button", { type: "button", disabled: disabled, onClick: () => handleSelect(option.id), className: clsx("flex items-center gap-4 px-3 py-2 text-left transition-colors", index !== buttons.length - 1 && "border-b border-gray-25", disabled && "cursor-not-allowed opacity-60"), children: [_jsx("span", { className: "flex-1 text-[16px] font-medium leading-[1.6] text-gray-900", children: option.id === BOT_DEFAULT_BUTTON_ID
|
|
24
|
+
? t("thread_detail.bot.restart")
|
|
25
|
+
: option.label }), _jsx(Icon, { icon: "angle-right-o", size: CHEVRON_SIZE, className: "text-gray-900" })] }, option.id))) })) : (_jsx("div", { className: "flex flex-col gap-2", children: buttons.map((option) => (_jsx("button", { type: "button", disabled: disabled, onClick: () => handleSelect(option.id), className: clsx("flex flex-1 items-center justify-center gap-2 rounded-lg border border-primary-400 bg-primary-25 px-3 py-2", "text-center text-[14px] font-[510] leading-[160%] text-primary-400 transition-colors", disabled && "cursor-not-allowed opacity-60"), children: _jsx("span", { children: option.id === BOT_DEFAULT_BUTTON_ID
|
|
26
|
+
? t("thread_detail.bot.restart")
|
|
27
|
+
: option.label }) }, option.id))) }));
|
|
28
|
+
return (_jsx("div", { className: "flex min-w-0 flex-col gap-2 px-3 py-1 sm:px-4", children: _jsx("div", { className: "flex min-w-0 items-end justify-start pl-3 pr-12", children: _jsx("div", { className: "flex min-w-0 max-w-[75%] flex-col items-start", children: _jsx(MessageBubble, { isMine: false, content: optionsNode }) }) }) }));
|
|
29
|
+
};
|
|
30
|
+
export default BotButtonMessage;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface BotStartButtonProps {
|
|
2
|
+
label: string;
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const BotStartButton: ({ label, onClick, disabled }: BotStartButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default BotStartButton;
|
|
8
|
+
//# sourceMappingURL=BotStartButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotStartButton.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotStartButton.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,8BAA8B,mBAAmB,4CAkBxE,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { Icon } from "../../../components/icon";
|
|
5
|
+
const ICON_SIZE = 20;
|
|
6
|
+
const BotStartButton = ({ label, onClick, disabled }) => {
|
|
7
|
+
return (_jsxs("button", { type: "button", onClick: onClick, disabled: disabled, className: clsx("flex h-12 w-full items-center justify-center gap-2 rounded-lg", "text-[16px] font-medium leading-[1.6] text-white transition-colors", disabled
|
|
8
|
+
? "cursor-not-allowed bg-primary-400/60"
|
|
9
|
+
: "bg-primary-400 active:bg-primary-400/90"), children: [_jsx(Icon, { icon: "chat-square-o", size: ICON_SIZE, className: "text-white" }), _jsx("span", { children: label })] }));
|
|
10
|
+
};
|
|
11
|
+
export default BotStartButton;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface BotSuggestProps {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
onRestart: () => void;
|
|
4
|
+
}
|
|
5
|
+
declare const BotSuggest: ({ disabled, onRestart }: BotSuggestProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default BotSuggest;
|
|
7
|
+
//# sourceMappingURL=BotSuggest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotSuggest.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotSuggest.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,QAAA,MAAM,UAAU,GAAI,yBAAyB,eAAe,4CAyB3D,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Icon } from "../../../components/icon";
|
|
6
|
+
const CHIP_ICON_SIZE = 14;
|
|
7
|
+
const BotSuggest = ({ disabled, onRestart }) => {
|
|
8
|
+
const { t } = useTranslation("conversation-inbox");
|
|
9
|
+
return (_jsx("div", { "data-testid": "conversation-bot-suggest", className: "flex w-full items-center gap-2 overflow-x-auto bg-chat-bubble-neutral/[0.05] px-2 pt-2", children: _jsxs("button", { type: "button", disabled: disabled, onClick: onRestart, className: clsx("flex h-7 shrink-0 items-center gap-1 rounded-lg bg-gray-25 px-3", "text-[12px] font-[510] leading-[1.6] tracking-[0.12px] text-gray-900 transition-colors", disabled && "cursor-not-allowed opacity-60"), children: [_jsx(Icon, { icon: "arrow-reload-o", size: CHIP_ICON_SIZE, className: "text-gray-900" }), _jsx("span", { className: "whitespace-nowrap", children: t("thread_detail.bot.restart") })] }) }));
|
|
10
|
+
};
|
|
11
|
+
export default BotSuggest;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as BotStartButton } from "./BotStartButton";
|
|
2
|
+
export type { BotStartButtonProps } from "./BotStartButton";
|
|
3
|
+
export { default as BotButtonMessage } from "./BotButtonMessage";
|
|
4
|
+
export type { BotButtonMessageProps } from "./BotButtonMessage";
|
|
5
|
+
export { default as BotSuggest } from "./BotSuggest";
|
|
6
|
+
export type { BotSuggestProps } from "./BotSuggest";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as ConversationDetailHeader } from "./ConversationDetailHeader";
|
|
2
|
+
export type { ConversationDetailHeaderProps } from "./ConversationDetailHeader";
|
|
3
|
+
export { default as ConversationDetailInput } from "./ConversationDetailInput";
|
|
4
|
+
export type { ConversationDetailInputProps } from "./ConversationDetailInput";
|
|
5
|
+
export { default as ConversationMessageList } from "./ConversationMessageList";
|
|
6
|
+
export { default as ConversationMoreOptionPanel } from "./ConversationMoreOptionPanel";
|
|
7
|
+
export type { ConversationMoreOptionPanelProps } from "./ConversationMoreOptionPanel";
|
|
8
|
+
export { default as ConversationMoreOptionItem } from "./ConversationMoreOptionItem";
|
|
9
|
+
export type { ConversationMoreOptionItemProps } from "./ConversationMoreOptionItem";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,YAAY,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAE9E,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAE/E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AACrF,YAAY,EAAE,+BAA+B,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as ConversationDetailHeader } from "./ConversationDetailHeader";
|
|
2
|
+
export { default as ConversationDetailInput } from "./ConversationDetailInput";
|
|
3
|
+
export { default as ConversationMessageList } from "./ConversationMessageList";
|
|
4
|
+
export { default as ConversationMoreOptionPanel } from "./ConversationMoreOptionPanel";
|
|
5
|
+
export { default as ConversationMoreOptionItem } from "./ConversationMoreOptionItem";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationInboxHeader.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxHeader.tsx"],"names":[],"mappings":"AAIA,QAAA,MAAM,uBAAuB,+CAkB5B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
const ConversationInboxHeader = () => {
|
|
5
|
+
const { t } = useTranslation("conversation-inbox");
|
|
6
|
+
return (_jsx("div", { "data-testid": "conversation-inbox-header", className: "flex w-full flex-col border-b border-gray-100 bg-white", children: _jsx("div", { className: "flex h-12 items-end px-3", children: _jsxs("div", { className: "flex h-full flex-col items-center justify-end px-3", children: [_jsx("span", { className: "pb-1.5 text-center text-[16px] font-bold leading-[160%] tracking-[0.16px] text-primary-400", children: t("tabs.messages") }), _jsx("span", { className: "h-[3px] w-full rounded-t bg-primary-400" })] }) }) }));
|
|
7
|
+
};
|
|
8
|
+
export default ConversationInboxHeader;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ConversationListItem } from "../../types/conversation";
|
|
2
|
+
export interface ConversationInboxItemProps {
|
|
3
|
+
item: ConversationListItem;
|
|
4
|
+
onClick: (item: ConversationListItem) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const ConversationInboxItem: ({ item, onClick }: ConversationInboxItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default ConversationInboxItem;
|
|
8
|
+
//# sourceMappingURL=ConversationInboxItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationInboxItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxItem.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAajE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC/C;AAQD,QAAA,MAAM,qBAAqB,GAAI,mBAAmB,0BAA0B,4CA2C3E,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import dayjs from "dayjs";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import useAuthStore from "../../store/auth";
|
|
6
|
+
import { formatTimestamp, parseLatestMessage } from "../../utils/common";
|
|
7
|
+
import { isBotPeer, resolveConversationAvatarUrl, resolvePeerBadgeLabel, resolveConversationDisplayName, } from "../../utils/conversation";
|
|
8
|
+
import ThreadCard from "../../components/conversation/ThreadCard";
|
|
9
|
+
import ConversationPeerAvatar from "./ConversationPeerAvatar";
|
|
10
|
+
import ConversationInboxTitle from "./ConversationInboxTitle";
|
|
11
|
+
const formatLatestMessageTime = (sendTime) => {
|
|
12
|
+
if (!sendTime)
|
|
13
|
+
return "";
|
|
14
|
+
const isToday = dayjs(sendTime).isSame(dayjs(), "day");
|
|
15
|
+
return formatTimestamp(sendTime, { hasTime: isToday });
|
|
16
|
+
};
|
|
17
|
+
const ConversationInboxItem = ({ item, onClick }) => {
|
|
18
|
+
var _a;
|
|
19
|
+
const { t } = useTranslation("conversation-inbox");
|
|
20
|
+
const { t: tCommon } = useTranslation("common");
|
|
21
|
+
const currentUserId = useAuthStore((state) => state.userID);
|
|
22
|
+
const displayName = resolveConversationDisplayName(item, t);
|
|
23
|
+
const avatarUrl = resolveConversationAvatarUrl(item);
|
|
24
|
+
const badgeLabel = resolvePeerBadgeLabel(item.peerType, t);
|
|
25
|
+
const latestMessagePreview = parseLatestMessage(item.latestMsg, currentUserId, tCommon, isBotPeer(item));
|
|
26
|
+
const latestMessageTime = formatLatestMessageTime(item.latestMsgSendTime || null);
|
|
27
|
+
const unreadCount = (_a = item.unreadCount) !== null && _a !== void 0 ? _a : 0;
|
|
28
|
+
return (_jsx(ThreadCard, { testId: "conversation-inbox-thread-card", onClick: () => onClick(item), unreadCount: unreadCount, time: latestMessageTime, preview: latestMessagePreview, avatar: _jsx(ConversationPeerAvatar, { peerType: item.peerType, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), title: _jsx(ConversationInboxTitle, { peerType: item.peerType, displayName: displayName, hasUnread: unreadCount > 0 }) }));
|
|
29
|
+
};
|
|
30
|
+
export default ConversationInboxItem;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConversationListItem } from "../../types/conversation";
|
|
2
|
+
export interface ConversationInboxListProps {
|
|
3
|
+
items: ConversationListItem[];
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
isFetchingNextPage: boolean;
|
|
6
|
+
hasNextPage: boolean;
|
|
7
|
+
onLoadMore: () => void;
|
|
8
|
+
onSelectThread?: (item: ConversationListItem) => void;
|
|
9
|
+
}
|
|
10
|
+
declare const ConversationInboxList: ({ items, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, onSelectThread, }: ConversationInboxListProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export default ConversationInboxList;
|
|
12
|
+
//# sourceMappingURL=ConversationInboxList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationInboxList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxList.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAKjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAYD,QAAA,MAAM,qBAAqB,GAAI,oFAO5B,0BAA0B,4CA8E5B,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|