@droppii-org/chat-sdk 0.1.6 → 0.1.7
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/cannedResponse/CannedResponseBody.d.ts.map +1 -1
- package/dist/components/cannedResponse/CannedResponseBody.js +54 -3
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +7 -2
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +22 -17
- package/dist/components/message/MessageHeader.d.ts +2 -2
- package/dist/components/message/MessageHeader.d.ts.map +1 -1
- package/dist/components/message/MessageHeader.js +5 -2
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +31 -22
- package/dist/components/message/SelectSession.d.ts.map +1 -1
- package/dist/components/message/SelectSession.js +7 -9
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
- package/dist/components/message/footer/ActionBar.js +3 -2
- package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -1
- package/dist/components/message/footer/CannedResponsePlugin.js +37 -1
- package/dist/components/message/footer/ComposerEditor.d.ts +7 -0
- package/dist/components/message/footer/ComposerEditor.d.ts.map +1 -0
- package/dist/components/message/footer/ComposerEditor.js +13 -0
- package/dist/components/message/footer/ComposerTabs.d.ts +9 -0
- package/dist/components/message/footer/ComposerTabs.d.ts.map +1 -0
- package/dist/components/message/footer/ComposerTabs.js +37 -0
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
- package/dist/components/message/footer/EnterHandler.js +10 -1
- package/dist/components/message/footer/index.d.ts +2 -2
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +45 -9
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +11 -1
- package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
- package/dist/components/session/DeskAssignedSession.js +14 -109
- package/dist/components/session/DeskTeamInbox.d.ts +3 -0
- package/dist/components/session/DeskTeamInbox.d.ts.map +1 -0
- package/dist/components/session/DeskTeamInbox.js +56 -0
- package/dist/components/session/SessionFilterMenu.d.ts +13 -0
- package/dist/components/session/SessionFilterMenu.d.ts.map +1 -0
- package/dist/components/session/SessionFilterMenu.js +27 -0
- package/dist/components/session/sessionMenuItems.d.ts +26 -0
- package/dist/components/session/sessionMenuItems.d.ts.map +1 -0
- package/dist/components/session/sessionMenuItems.js +108 -0
- package/dist/hooks/conversation/useConversationPreview.d.ts +12 -0
- package/dist/hooks/conversation/useConversationPreview.d.ts.map +1 -0
- package/dist/hooks/conversation/useConversationPreview.js +22 -0
- package/dist/hooks/message/useConversationMessages.d.ts +27 -0
- package/dist/hooks/message/useConversationMessages.d.ts.map +1 -0
- package/dist/hooks/message/useConversationMessages.js +29 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/usePullSessionMessages.d.ts +9 -0
- package/dist/hooks/message/usePullSessionMessages.d.ts.map +1 -0
- package/dist/hooks/message/usePullSessionMessages.js +27 -0
- package/dist/hooks/message/useSendMessage.d.ts +8 -6
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/message/useSendMessage.js +8 -8
- package/dist/hooks/session/useConversationSessionState.d.ts +21 -0
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -0
- package/dist/hooks/session/useConversationSessionState.js +41 -0
- package/dist/hooks/session/useGetSession.d.ts.map +1 -1
- package/dist/hooks/session/useGetSession.js +138 -52
- package/dist/hooks/session/useGetTeamSessionSummary.d.ts +3 -0
- package/dist/hooks/session/useGetTeamSessionSummary.d.ts.map +1 -0
- package/dist/hooks/session/useGetTeamSessionSummary.js +12 -0
- package/dist/hooks/session/useIsJoinedGroup.d.ts +5 -0
- package/dist/hooks/session/useIsJoinedGroup.d.ts.map +1 -0
- package/dist/hooks/session/useIsJoinedGroup.js +24 -0
- package/dist/hooks/session/useJoinGroupFlow.d.ts +12 -0
- package/dist/hooks/session/useJoinGroupFlow.d.ts.map +1 -0
- package/dist/hooks/session/useJoinGroupFlow.js +59 -0
- package/dist/hooks/session/useJoinSession.d.ts +3 -0
- package/dist/hooks/session/useJoinSession.d.ts.map +1 -0
- package/dist/hooks/session/useJoinSession.js +38 -0
- package/dist/hooks/user/useCurrentUserAccountType.d.ts +3 -0
- package/dist/hooks/user/useCurrentUserAccountType.d.ts.map +1 -0
- package/dist/hooks/user/useCurrentUserAccountType.js +30 -0
- package/dist/locales/vi/common.json +12 -2
- package/dist/services/query.d.ts +5 -0
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +5 -0
- package/dist/services/routes.d.ts +5 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +5 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +41 -12
- package/dist/store/session.js +1 -1
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/chat.d.ts +18 -1
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/chat.js +9 -0
- package/dist/types/dto.d.ts +87 -0
- package/dist/types/dto.d.ts.map +1 -1
- package/dist/utils/events.d.ts +1 -0
- package/dist/utils/events.d.ts.map +1 -1
- package/dist/utils/messageTransform.d.ts +5 -0
- package/dist/utils/messageTransform.d.ts.map +1 -0
- package/dist/utils/messageTransform.js +106 -0
- package/dist/utils/queryHelpers.d.ts.map +1 -1
- package/dist/utils/queryHelpers.js +2 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CannedResponseBody.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseBody.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CannedResponseBody.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseBody.tsx"],"names":[],"mappings":"AAiBA,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AASD,QAAA,MAAM,kBAAkB,GAAI,OAAO,wBAAwB,4CA8M1D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Empty, Segmented, Spin } from "antd";
|
|
3
3
|
import { useFetchMyTeams } from "../../hooks/team/useFetchMyTeam";
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { CannedResponseVisibleScope } from "../../types/chat";
|
|
7
7
|
import TeamItem from "./team/TeamItem";
|
|
8
8
|
import { useFetchCannedResponse } from "../../hooks/cannedResponse/useFetchCannedResponse";
|
|
@@ -10,9 +10,24 @@ import InfiniteScroll from "react-infinite-scroll-component";
|
|
|
10
10
|
import { Icon } from "../icon";
|
|
11
11
|
import { sanitizeHtml } from "../../utils/common";
|
|
12
12
|
import { useDebounce } from "ahooks";
|
|
13
|
+
import clsx from "clsx";
|
|
14
|
+
import emitter from "../../utils/events";
|
|
15
|
+
import { isArray } from "lodash";
|
|
16
|
+
import { useMessageFooterContext } from "../message/footer";
|
|
17
|
+
const parseIndex = (index, length) => {
|
|
18
|
+
if (index === null)
|
|
19
|
+
return null;
|
|
20
|
+
if (index < 0)
|
|
21
|
+
return 0;
|
|
22
|
+
if (index >= length)
|
|
23
|
+
return length - 1;
|
|
24
|
+
return index;
|
|
25
|
+
};
|
|
13
26
|
const CannedResponseBody = (props) => {
|
|
14
27
|
var _a, _b;
|
|
15
28
|
const { search, onSelectCannedResponse, cannedQuery } = props;
|
|
29
|
+
const { activeCannedIndex, setActiveCannedIndex, setMaxActiveCannedIndex } = useMessageFooterContext();
|
|
30
|
+
const itemRefs = useRef([]);
|
|
16
31
|
const { t } = useTranslation();
|
|
17
32
|
const { data: myTeams, isLoading: isLoadingTeams } = useFetchMyTeams();
|
|
18
33
|
const [scope, setScope] = useState(CannedResponseVisibleScope.TEAM);
|
|
@@ -25,6 +40,9 @@ const CannedResponseBody = (props) => {
|
|
|
25
40
|
categoryId: selectedCategoryId,
|
|
26
41
|
shortcut: search || cannedQueryDebounced || "",
|
|
27
42
|
});
|
|
43
|
+
const validActiveCannedIndex = useMemo(() => {
|
|
44
|
+
return parseIndex(activeCannedIndex, (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0);
|
|
45
|
+
}, [activeCannedIndex, cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length]);
|
|
28
46
|
const options = useMemo(() => {
|
|
29
47
|
return [
|
|
30
48
|
{ label: t("team"), value: CannedResponseVisibleScope.TEAM },
|
|
@@ -43,12 +61,45 @@ const CannedResponseBody = (props) => {
|
|
|
43
61
|
setSelectedTeamId(myTeams[0].teamId);
|
|
44
62
|
}
|
|
45
63
|
}, [isLoadingTeams, myTeams]);
|
|
46
|
-
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const handler = (index) => {
|
|
66
|
+
var _a;
|
|
67
|
+
const validIndex = parseIndex(index, cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length);
|
|
68
|
+
if (validIndex !== null && !!(cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses[validIndex])) {
|
|
69
|
+
const htmlContent = ((_a = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses[validIndex]) === null || _a === void 0 ? void 0 : _a.content) || "";
|
|
70
|
+
// 🔒 Sanitize HTML to prevent XSS attacks
|
|
71
|
+
const sanitizedContent = sanitizeHtml(htmlContent);
|
|
72
|
+
onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
emitter.on("CANNED_RESPONSE_SELECT", handler);
|
|
76
|
+
return () => {
|
|
77
|
+
emitter.off("CANNED_RESPONSE_SELECT", handler);
|
|
78
|
+
};
|
|
79
|
+
}, [cannedResponses, onSelectCannedResponse]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
setActiveCannedIndex(0);
|
|
82
|
+
}, [search, cannedQueryDebounced, setActiveCannedIndex]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
if (activeCannedIndex !== null) {
|
|
86
|
+
(_b = (_a = itemRefs.current) === null || _a === void 0 ? void 0 : _a[activeCannedIndex]) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
|
|
87
|
+
block: "nearest",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}, [activeCannedIndex]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
setMaxActiveCannedIndex((cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) - 1 > 0 ? (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) - 1 : 0);
|
|
93
|
+
}, [cannedResponses, setMaxActiveCannedIndex]);
|
|
94
|
+
return (_jsxs("div", { className: "flex flex-1 flex-row h-[400px]", children: [_jsxs("div", { className: "w-[170px] p-2 border-r border-gray-200", children: [_jsx(Segmented, { options: options, block: true, size: "small", value: scope, onChange: setScope }), _jsx("div", { className: "overflow-y-auto h-full", children: scope === CannedResponseVisibleScope.TEAM && (_jsx("div", { className: "mt-1 flex flex-col", children: (_a = myTeams === null || myTeams === void 0 ? void 0 : myTeams.map) === null || _a === void 0 ? void 0 : _a.call(myTeams, (team, index) => renderTeamItem(team, index)) })) })] }), _jsxs("div", { id: "scrollableCannedDiv", className: "flex flex-col flex-1 h-full overflow-y-auto", children: [!!cannedQuery && !search && (_jsx("div", { className: "px-3 py-2 border-b border-gray-200", children: _jsxs("span", { className: "text-sm text-gray-500", children: [t("canned_response_quick_search_placeholder"), _jsx("span", { className: "text-blue-600", children: ` #${cannedQuery}` })] }) })), _jsx(InfiniteScroll, { dataLength: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableCannedDiv", children: !isArray(cannedResponses) || (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_canned_response") }) })) : ((_b = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.map) === null || _b === void 0 ? void 0 : _b.call(cannedResponses, (canned, index) => {
|
|
47
95
|
var _a, _b;
|
|
96
|
+
const isActive = index === validActiveCannedIndex;
|
|
48
97
|
const htmlContent = (canned === null || canned === void 0 ? void 0 : canned.content) || "";
|
|
49
98
|
// 🔒 Sanitize HTML to prevent XSS attacks
|
|
50
99
|
const sanitizedContent = sanitizeHtml(htmlContent);
|
|
51
|
-
return (_jsxs("div", {
|
|
100
|
+
return (_jsxs("div", { ref: (el) => {
|
|
101
|
+
itemRefs.current[index] = el;
|
|
102
|
+
}, className: clsx("flex flex-col flex-1 px-3 py-2 cursor-pointer hover:bg-blue-100 w-full border-b border-gray-200", isActive && "bg-blue-100"), onClick: () => onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent), children: [_jsxs("span", { className: "text-xs truncate text-gray-500", children: [`${((_a = canned === null || canned === void 0 ? void 0 : canned.category) === null || _a === void 0 ? void 0 : _a.name)
|
|
52
103
|
? `${(_b = canned === null || canned === void 0 ? void 0 : canned.category) === null || _b === void 0 ? void 0 : _b.name} / `
|
|
53
104
|
: ""}`, _jsx("span", { className: "text-black", children: `${(canned === null || canned === void 0 ? void 0 : canned.name) || ""} ` }), _jsx("span", { children: "/" }), _jsx("span", { className: "text-blue-500", children: ` #${(canned === null || canned === void 0 ? void 0 : canned.shortcut) || ""}` })] }), _jsx("div", { dangerouslySetInnerHTML: {
|
|
54
105
|
__html: sanitizedContent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AASpD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,kBAEhC,8BAA8B,mDAgGhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
|
|
@@ -6,6 +6,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
|
6
6
|
import { Avatar, Badge } from "antd";
|
|
7
7
|
import { useChatContext } from "../../context/ChatContext";
|
|
8
8
|
import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
|
|
9
|
+
import { useConversationPreview } from "../../hooks/conversation/useConversationPreview";
|
|
9
10
|
import { formatTimestamp, parseLatestMessage } from "../../utils/common";
|
|
10
11
|
const ConversationBySessionItem = ({ sessionItem, }) => {
|
|
11
12
|
var _a;
|
|
@@ -30,10 +31,14 @@ const ConversationBySessionItem = ({ sessionItem, }) => {
|
|
|
30
31
|
setSelectedConversationId(conversation.conversationID);
|
|
31
32
|
};
|
|
32
33
|
const { avatar, displayName = "" } = useConversationDisplayData(conversation || null);
|
|
34
|
+
const { latestMsg, latestMsgSendTime } = useConversationPreview({
|
|
35
|
+
conversation,
|
|
36
|
+
ownerUserID: sessionItem.ownerId,
|
|
37
|
+
});
|
|
33
38
|
if (!conversation)
|
|
34
39
|
return null;
|
|
35
|
-
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(Badge, { dot: true, status: "success", offset: [-2, 36], 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(
|
|
40
|
+
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(Badge, { dot: true, status: "success", offset: [-2, 36], 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, {
|
|
36
41
|
hasTime: true,
|
|
37
|
-
}) }), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.conversationID));
|
|
42
|
+
}) })), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.conversationID));
|
|
38
43
|
};
|
|
39
44
|
export default ConversationBySessionItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAoBA,QAAA,MAAM,oBAAoB,+CAoMzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -12,10 +12,12 @@ import useSessionStore from "../../store/session";
|
|
|
12
12
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
13
13
|
import ConversationBySessionItem from "./ConversationBySessionItem";
|
|
14
14
|
import { useGetSession } from "../../hooks/session/useGetSession";
|
|
15
|
+
import { useGetTeamSessionSummary } from "../../hooks/session/useGetTeamSessionSummary";
|
|
15
16
|
import { DChatSDK } from "../../constants/sdk";
|
|
16
17
|
import emitter from "../../utils/events";
|
|
18
|
+
import { getFilterConfig } from "../session/sessionMenuItems";
|
|
17
19
|
const DeskConversationList = () => {
|
|
18
|
-
var _a;
|
|
20
|
+
var _a, _b;
|
|
19
21
|
const searchInputRef = useRef(null);
|
|
20
22
|
const { t } = useTranslation();
|
|
21
23
|
const [search, setSearch] = useState("");
|
|
@@ -26,9 +28,13 @@ const DeskConversationList = () => {
|
|
|
26
28
|
const conversationList = useConversationStore((state) => state.conversationList);
|
|
27
29
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
28
30
|
const filterSummary = useSessionStore((state) => state.filterSummary);
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const { data: teamSummaries } = useGetTeamSessionSummary();
|
|
32
|
+
const { dataFlatten: sessions, hasNextPage, fetchNextPage, refetch } = useGetSession({ filter: filterSummary });
|
|
33
|
+
const currentFilterKey = filterSummary.status || filterSummary.tag || "";
|
|
34
|
+
const filterConfig = getFilterConfig(currentFilterKey);
|
|
35
|
+
const pathName = filterSummary.teamId
|
|
36
|
+
? ((_a = teamSummaries === null || teamSummaries === void 0 ? void 0 : teamSummaries.find((team) => team.teamId === filterSummary.teamId)) === null || _a === void 0 ? void 0 : _a.teamName) || ""
|
|
37
|
+
: t("my_messages");
|
|
32
38
|
const debouncedSearch = useDebounce(search, { wait: 500 });
|
|
33
39
|
const onCloseSearch = () => {
|
|
34
40
|
setSearch("");
|
|
@@ -65,28 +71,27 @@ const DeskConversationList = () => {
|
|
|
65
71
|
}
|
|
66
72
|
}, [searchParams, conversationList]);
|
|
67
73
|
useEffect(() => {
|
|
68
|
-
|
|
74
|
+
const handler = (sessionUpdated) => {
|
|
69
75
|
if (sessionUpdated.status !== filterSummary.status ||
|
|
70
76
|
sessionUpdated.tag !== filterSummary.tag) {
|
|
71
77
|
refetch();
|
|
72
78
|
}
|
|
73
|
-
}
|
|
79
|
+
};
|
|
80
|
+
emitter.on("UPDATE_SESSION", handler);
|
|
74
81
|
return () => {
|
|
75
|
-
emitter.off("UPDATE_SESSION",
|
|
76
|
-
refetch();
|
|
77
|
-
});
|
|
82
|
+
emitter.off("UPDATE_SESSION", handler);
|
|
78
83
|
};
|
|
79
|
-
}, [filterSummary]);
|
|
80
|
-
return (_jsxs("div", { className:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
}, [filterSummary, refetch]);
|
|
85
|
+
return (_jsxs("div", { className: "flex flex-col h-full bg-white border-r border-gray-200 w-[320px]", children: [_jsxs("div", { className: "p-3 border-b border-gray-100", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { ref: searchInputRef, placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), onChange: (e) => {
|
|
86
|
+
if (!showSearch && e.target.value) {
|
|
87
|
+
setShowSearchTrue();
|
|
88
|
+
}
|
|
89
|
+
setSearch(e.target.value);
|
|
90
|
+
}, className: "rounded-lg text-sm flex-1 h-[36px]", size: "large", allowClear: true, onClick: setShowSearchTrue, value: search, autoFocus: false, onClear: onCloseSearch }), showSearch && (_jsx(Button, { onClick: onCloseSearch, variant: "outlined", className: "p-0 w-[36px] h-[36px] text-gray-500", children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] }), _jsxs("div", { className: "flex items-center justify-between mt-2", children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate", children: pathName }), filterConfig && (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: filterConfig.iconName, size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t(filterConfig.labelKey) })] }))] })] }), _jsxs("div", { id: "scrollableConversationsDiv", style: {
|
|
86
91
|
height: "100%",
|
|
87
92
|
overflow: showSearch ? "hidden" : "auto",
|
|
88
93
|
position: "relative",
|
|
89
|
-
}, children: [_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((
|
|
94
|
+
}, children: [_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((_b = sessions === null || sessions === void 0 ? void 0 : sessions.map) === null || _b === void 0 ? void 0 : _b.call(sessions, (session) => (_jsx(ConversationBySessionItem, { sessionItem: session }, session.conversationId)))) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
|
|
90
95
|
body: {
|
|
91
96
|
padding: 0,
|
|
92
97
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SessionStatus, SessionTag } from "../../types/chat";
|
|
2
|
-
import {
|
|
2
|
+
import { ISessionResponse } from "../../types/dto";
|
|
3
3
|
interface MessageHeaderProps {
|
|
4
4
|
onClose?: () => void;
|
|
5
|
-
currentSession?:
|
|
5
|
+
currentSession?: ISessionResponse;
|
|
6
6
|
}
|
|
7
7
|
type SelectSessionValueType = SessionStatus | SessionTag;
|
|
8
8
|
export interface SelectSessionOption {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKnD,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED,KAAK,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,QAAA,MAAM,aAAa,GAAI,6BAA6B,kBAAkB,4CAoMrE,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -12,13 +12,16 @@ import SelectSession from "./SelectSession";
|
|
|
12
12
|
import { useUpdateSession } from "../../hooks/session/useUpdateSession";
|
|
13
13
|
import useAuthStore from "../../store/auth";
|
|
14
14
|
import SearchDrawer from "../searchConversation/SearchDrawer";
|
|
15
|
+
import { isGroupSession } from "../../utils/imCommon";
|
|
15
16
|
const MessageHeader = ({ onClose, currentSession }) => {
|
|
16
|
-
var _a;
|
|
17
|
+
var _a, _b;
|
|
17
18
|
const { t } = useTranslation();
|
|
18
19
|
const isCx = useAuthStore((state) => state.isCx);
|
|
19
20
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
21
|
+
const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
|
|
20
22
|
const { mutate: updateSession } = useUpdateSession();
|
|
21
23
|
const { avatar, displayName } = useConversationDisplayData(conversationData);
|
|
24
|
+
const shouldShowMemberCount = isGroupSession(conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType);
|
|
22
25
|
const [currentSessionStatus, setCurrentSessionStatus] = useState(SessionStatus.UNASSIGNED);
|
|
23
26
|
const [currentSessionTag, setCurrentSessionTag] = useState(SessionTag.NONE);
|
|
24
27
|
const statusOptions = useMemo(() => {
|
|
@@ -122,6 +125,6 @@ const MessageHeader = ({ onClose, currentSession }) => {
|
|
|
122
125
|
setCurrentSessionStatus(currentSession.status);
|
|
123
126
|
}
|
|
124
127
|
}, [currentSession]);
|
|
125
|
-
return (_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3 bg-white flex-wrap", children: [_jsx(Avatar, { src: avatar, size: "large", className: "min-w-10 min-h-10", children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), _jsxs("div", { className: "flex flex-col overflow-hidden flex-1", children: [_jsx("p", { className: "text-base truncate", children: displayName || "" }), _jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count:
|
|
128
|
+
return (_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3 bg-white flex-wrap", children: [_jsx(Avatar, { src: avatar, size: "large", className: "min-w-10 min-h-10", children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), _jsxs("div", { className: "flex flex-col overflow-hidden flex-1", children: [_jsx("p", { className: "text-base truncate", children: displayName || "" }), shouldShowMemberCount && (_jsx("p", { className: "text-xs text-gray-500 truncate", children: t("member_count", { count: (_b = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.memberCount) !== null && _b !== void 0 ? _b : 0 }) }))] }), _jsxs("div", { className: "flex items-center gap-2 justify-end overflow-hidden", children: [isCx && (_jsx(SelectSession, { placeholder: t("select_tag"), options: tagOptions, value: currentSessionTag, onChange: (value) => handleUpdateSession(value, "tag", value === currentSessionTag), excludeOptions: [SessionTag.SLOW_PROCESSING, SessionTag.NONE] })), isCx && (_jsx(SelectSession, { placeholder: t("select_status"), options: statusOptions, value: currentSessionStatus, onChange: (value) => handleUpdateSession(value, "status", value === currentSessionStatus) })), _jsx(SearchDrawer, {}), _jsx(MediaCollection, {}), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "align-justify-o", size: 22 }) }), !!onClose && (_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] })] }));
|
|
126
129
|
};
|
|
127
130
|
export default MessageHeader;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"
|
|
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;AAGD,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CA4U3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import { Empty, Modal, Spin } from "antd";
|
|
5
|
-
import { useCallback, useEffect,
|
|
3
|
+
import { useConversationMessages } from "../../hooks/message/useConversationMessages";
|
|
4
|
+
import { Button, Empty, Modal, Spin } from "antd";
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
6
|
import dayjs from "dayjs";
|
|
7
7
|
import isToday from "dayjs/plugin/isToday";
|
|
8
8
|
import emitter from "../../utils/events";
|
|
@@ -18,8 +18,9 @@ import { useBoolean, useDebounceFn } from "ahooks";
|
|
|
18
18
|
import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../constants";
|
|
19
19
|
import { markConversationMessageAsRead } from "../../hooks/conversation/useConversation";
|
|
20
20
|
import { useChatContext } from "../../context/ChatContext";
|
|
21
|
-
import { useGetSession } from "../../hooks/session/useGetSession";
|
|
22
21
|
import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
|
|
22
|
+
import { useConversationSessionState } from "../../hooks/session/useConversationSessionState";
|
|
23
|
+
import { useJoinGroupFlow } from "../../hooks/session/useJoinGroupFlow";
|
|
23
24
|
dayjs.extend(isToday);
|
|
24
25
|
const BOTTOM_THRESHOLD = -5;
|
|
25
26
|
const MessageList = (props) => {
|
|
@@ -28,19 +29,31 @@ const MessageList = (props) => {
|
|
|
28
29
|
const { user } = useChatContext();
|
|
29
30
|
const { onClose, conversationId, searchClientMsgID, openCreateCannedModal } = props;
|
|
30
31
|
const scrollRef = useRef(null);
|
|
31
|
-
const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useMessage(conversationId, searchClientMsgID);
|
|
32
32
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
33
33
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
34
34
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
35
|
-
const { dataFlatten: sessions, refetch: refetchSession } = useGetSession({
|
|
36
|
-
filter: {
|
|
37
|
-
conversationIds: !!conversationId ? [conversationId] : [],
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
35
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
41
36
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
42
37
|
const [showConfirmRevoke, { setTrue: openConfirmRevoke, setFalse: closeConfirmRevoke },] = useBoolean(false);
|
|
43
38
|
const { revokeMessage, loading: isRevoking } = useRevokeMessage();
|
|
39
|
+
const { isNotGroupMember, latestConversationSession, customerUserID, refetchConversationSessions, refetchIsJoined, } = useConversationSessionState({
|
|
40
|
+
conversationId,
|
|
41
|
+
conversationType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType,
|
|
42
|
+
groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
|
|
43
|
+
userID: user === null || user === void 0 ? void 0 : user.userID,
|
|
44
|
+
});
|
|
45
|
+
const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useConversationMessages({
|
|
46
|
+
conversationId,
|
|
47
|
+
searchClientMsgID,
|
|
48
|
+
restrictToLastSession: isNotGroupMember,
|
|
49
|
+
customerUserID,
|
|
50
|
+
});
|
|
51
|
+
const { handleJoinGroup, isJoining } = useJoinGroupFlow({
|
|
52
|
+
groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
|
|
53
|
+
latestConversationSessionId: latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.id,
|
|
54
|
+
refetchConversationSessions,
|
|
55
|
+
refetchIsJoined,
|
|
56
|
+
});
|
|
44
57
|
const handleOpenRevoke = useCallback((clientMsgID) => {
|
|
45
58
|
setSelectedItem(clientMsgID);
|
|
46
59
|
openConfirmRevoke();
|
|
@@ -58,9 +71,6 @@ const MessageList = (props) => {
|
|
|
58
71
|
revokeMessage,
|
|
59
72
|
handleCloseRevoke,
|
|
60
73
|
]);
|
|
61
|
-
const currentSession = useMemo(() => {
|
|
62
|
-
return sessions === null || sessions === void 0 ? void 0 : sessions.find((session) => session.conversationId === conversationId);
|
|
63
|
-
}, [sessions, conversationId]);
|
|
64
74
|
const handleMarkConversationMessageAsRead = () => {
|
|
65
75
|
var _a, _b, _c, _d, _e;
|
|
66
76
|
const lastMessage = (_b = (_a = latestLoadState === null || latestLoadState === void 0 ? void 0 : latestLoadState.current) === null || _a === void 0 ? void 0 : _a.messageList) === null || _b === void 0 ? void 0 : _b[0];
|
|
@@ -136,17 +146,16 @@ const MessageList = (props) => {
|
|
|
136
146
|
};
|
|
137
147
|
}, []);
|
|
138
148
|
useEffect(() => {
|
|
139
|
-
|
|
149
|
+
const handler = (sessionUpdated) => {
|
|
140
150
|
if (sessionUpdated.conversationId === conversationId) {
|
|
141
|
-
|
|
151
|
+
refetchConversationSessions();
|
|
142
152
|
}
|
|
143
|
-
}
|
|
153
|
+
};
|
|
154
|
+
emitter.on("UPDATE_SESSION", handler);
|
|
144
155
|
return () => {
|
|
145
|
-
emitter.off("UPDATE_SESSION",
|
|
146
|
-
refetchSession();
|
|
147
|
-
});
|
|
156
|
+
emitter.off("UPDATE_SESSION", handler);
|
|
148
157
|
};
|
|
149
|
-
}, [conversationId]);
|
|
158
|
+
}, [conversationId, refetchConversationSessions]);
|
|
150
159
|
useEffect(() => {
|
|
151
160
|
if (!loadState.hasMoreNew && !loadState.initLoading) {
|
|
152
161
|
handleMarkConversationMessageAsRead();
|
|
@@ -174,7 +183,7 @@ const MessageList = (props) => {
|
|
|
174
183
|
backgroundSize: "cover",
|
|
175
184
|
backgroundPosition: "center",
|
|
176
185
|
overflowX: "hidden",
|
|
177
|
-
}, children: [_jsx(MessageHeader, { onClose: onClose, currentSession:
|
|
186
|
+
}, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession }), _jsx("div", { id: "scrollableMessagesDiv", ref: scrollRef, style: {
|
|
178
187
|
height: "100%",
|
|
179
188
|
overflowY: "auto",
|
|
180
189
|
overflowX: "hidden",
|
|
@@ -192,6 +201,6 @@ const MessageList = (props) => {
|
|
|
192
201
|
handleMarkConversationMessageAsRead();
|
|
193
202
|
loadMoreNewMessage();
|
|
194
203
|
}
|
|
195
|
-
}, 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, {}) })), _jsx(MessageFooter, { currentSession:
|
|
204
|
+
}, 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, {}) })), isNotGroupMember ? (_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: handleJoinGroup, 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") }) })] }));
|
|
196
205
|
};
|
|
197
206
|
export default MessageList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectSession.d.ts","sourceRoot":"","sources":["../../../src/components/message/SelectSession.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SelectSession.d.ts","sourceRoot":"","sources":["../../../src/components/message/SelectSession.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,UAAU,kBAAkB;IAC1B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,aAAa,GAAI,4DAMpB,kBAAkB,4CAwDpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -3,23 +3,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Select } from "antd";
|
|
4
4
|
import { Icon } from "../icon";
|
|
5
5
|
import clsx from "clsx";
|
|
6
|
-
import { useTranslation } from "react-i18next";
|
|
7
6
|
const SelectSession = ({ options, value, onChange, excludeOptions, placeholder, }) => {
|
|
8
|
-
const { t } = useTranslation();
|
|
9
7
|
const selectedOption = options.find((option) => option.value === value);
|
|
10
|
-
|
|
11
|
-
const filteredOptions = options.filter((option) => !(excludeOptions === null || excludeOptions === void 0 ? void 0 : excludeOptions.includes(option.value)));
|
|
12
|
-
// Transform options for Ant Design Select
|
|
8
|
+
const renderOptionLabel = (option) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: clsx("w-2 h-2 rounded-full", option.tintColorClassnameBg) }), _jsx("span", { className: clsx("text-xs truncate flex-1", option.tintColorClassname), children: option.label })] }));
|
|
9
|
+
const filteredOptions = options.filter((option) => option.value === value || !(excludeOptions === null || excludeOptions === void 0 ? void 0 : excludeOptions.includes(option.value)));
|
|
13
10
|
const selectOptions = filteredOptions.map((option) => ({
|
|
14
|
-
label: (
|
|
11
|
+
label: renderOptionLabel(option),
|
|
15
12
|
value: option.value,
|
|
16
13
|
}));
|
|
17
|
-
// Handle selection change
|
|
18
14
|
const handleSelect = (val) => {
|
|
19
15
|
onChange(val);
|
|
20
16
|
};
|
|
21
|
-
return (_jsx("div", { className: "relative", children: _jsx(Select, { className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.bgTintColorClassname} custom-select min-w-[100px]`, placement: "bottomLeft", placeholder: placeholder, options: selectOptions, value: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value, onSelect: handleSelect, suffixIcon: _jsx(Icon, { icon: "angle-down-o", className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.tintColorClassname} mt-[2px]`, size: 14 }), menuItemSelectedIcon: null, labelRender: (
|
|
17
|
+
return (_jsx("div", { className: "relative", children: _jsx(Select, { className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.bgTintColorClassname} custom-select min-w-[100px]`, placement: "bottomLeft", placeholder: placeholder, options: selectOptions, value: selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value, onSelect: handleSelect, suffixIcon: _jsx(Icon, { icon: "angle-down-o", className: `${selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.tintColorClassname} mt-[2px]`, size: 14 }), menuItemSelectedIcon: null, labelRender: () => ["NONE", ""].includes(selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value)
|
|
22
18
|
? placeholder
|
|
23
|
-
:
|
|
19
|
+
: selectedOption
|
|
20
|
+
? renderOptionLabel(selectedOption)
|
|
21
|
+
: undefined }) }));
|
|
24
22
|
};
|
|
25
23
|
export default SelectSession;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAsBA,QAAA,MAAM,SAAS,+
|
|
1
|
+
{"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAsBA,QAAA,MAAM,SAAS,+CA2Gd,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -13,7 +13,7 @@ import MediaActions from "./MediaActions";
|
|
|
13
13
|
import useConversationStore from "../../../store/conversation";
|
|
14
14
|
const ActionBar = () => {
|
|
15
15
|
const [editor] = useLexicalComposerContext();
|
|
16
|
-
const { onSendMessage, listUploadFiles, setListUploadFiles } = useMessageFooterContext();
|
|
16
|
+
const { onSendMessage, isInternal, listUploadFiles, setListUploadFiles } = useMessageFooterContext();
|
|
17
17
|
const quotedMessage = useConversationStore((state) => state.quotedMessage);
|
|
18
18
|
const [isEmptyInput, setIsEmptyInput] = useState(true);
|
|
19
19
|
const canSend = useMemo(() => {
|
|
@@ -34,6 +34,7 @@ const ActionBar = () => {
|
|
|
34
34
|
plainText,
|
|
35
35
|
richText,
|
|
36
36
|
type: listUploadFiles.length > 0 ? "file" : "text",
|
|
37
|
+
isInternal,
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
editor.update(() => {
|
|
@@ -71,7 +72,7 @@ const ActionBar = () => {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
});
|
|
74
|
-
}, [editor, onSendMessage, canSend]);
|
|
75
|
+
}, [editor, onSendMessage, canSend, isInternal, listUploadFiles.length]);
|
|
75
76
|
useEffect(() => {
|
|
76
77
|
return editor.registerUpdateListener(({ editorState }) => {
|
|
77
78
|
editorState.read(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CannedResponsePlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/CannedResponsePlugin.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CannedResponsePlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/CannedResponsePlugin.tsx"],"names":[],"mappings":"AAWA,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,QAsFrE"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
3
|
-
import { $getSelection, $isRangeSelection } from "lexical";
|
|
3
|
+
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, } from "lexical";
|
|
4
|
+
import { useMessageFooterContext } from ".";
|
|
4
5
|
export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
|
|
5
6
|
const [editor] = useLexicalComposerContext();
|
|
7
|
+
const { activeCannedIndex, setActiveCannedIndex, isOpenCanned, maxActiveCannedIndex, } = useMessageFooterContext();
|
|
6
8
|
useEffect(() => {
|
|
7
9
|
return editor.registerUpdateListener(({ editorState }) => {
|
|
8
10
|
editorState.read(() => {
|
|
@@ -27,5 +29,39 @@ export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
|
|
|
27
29
|
});
|
|
28
30
|
});
|
|
29
31
|
}, [editor, onOpen, onClose]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const unregisterUp = editor.registerCommand(KEY_ARROW_UP_COMMAND, (e) => {
|
|
34
|
+
if (!isOpenCanned)
|
|
35
|
+
return false;
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
setActiveCannedIndex(activeCannedIndex === null
|
|
38
|
+
? null
|
|
39
|
+
: activeCannedIndex - 1 < 0
|
|
40
|
+
? 0
|
|
41
|
+
: activeCannedIndex - 1);
|
|
42
|
+
return true;
|
|
43
|
+
}, COMMAND_PRIORITY_LOW);
|
|
44
|
+
const unregisterDown = editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (e) => {
|
|
45
|
+
if (!isOpenCanned)
|
|
46
|
+
return false;
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
setActiveCannedIndex(activeCannedIndex === null
|
|
49
|
+
? 0
|
|
50
|
+
: activeCannedIndex + 1 > maxActiveCannedIndex
|
|
51
|
+
? maxActiveCannedIndex
|
|
52
|
+
: activeCannedIndex + 1);
|
|
53
|
+
return true;
|
|
54
|
+
}, COMMAND_PRIORITY_LOW);
|
|
55
|
+
return () => {
|
|
56
|
+
unregisterUp();
|
|
57
|
+
unregisterDown();
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
editor,
|
|
61
|
+
isOpenCanned,
|
|
62
|
+
activeCannedIndex,
|
|
63
|
+
setActiveCannedIndex,
|
|
64
|
+
maxActiveCannedIndex,
|
|
65
|
+
]);
|
|
30
66
|
return null;
|
|
31
67
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface ComposerEditorProps {
|
|
2
|
+
isInternal: boolean;
|
|
3
|
+
placeholderText: string;
|
|
4
|
+
}
|
|
5
|
+
declare const ComposerEditor: ({ isInternal, placeholderText, }: ComposerEditorProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ComposerEditor;
|
|
7
|
+
//# sourceMappingURL=ComposerEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComposerEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ComposerEditor.tsx"],"names":[],"mappings":"AAOA,UAAU,mBAAmB;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,QAAA,MAAM,cAAc,GAAI,kCAGrB,mBAAmB,4CAwBrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
4
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
5
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
const ComposerEditor = ({ isInternal, placeholderText, }) => {
|
|
8
|
+
const editorClasses = clsx("min-h-[64px] max-h-[140px] overflow-y-auto rounded-md border px-3 py-2 text-sm", isInternal
|
|
9
|
+
? "border-orange-300 bg-orange-50"
|
|
10
|
+
: "border-indigo-500 bg-blue-100");
|
|
11
|
+
return (_jsx("div", { className: "px-4", children: _jsx("div", { className: "relative", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: editorClasses }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": placeholderText, placeholder: _jsx("div", { className: "absolute left-3 top-2 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: placeholderText }) }) }) }) }));
|
|
12
|
+
};
|
|
13
|
+
export default ComposerEditor;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface ComposerTabsProps {
|
|
2
|
+
canUseInternal: boolean;
|
|
3
|
+
hintText: string;
|
|
4
|
+
isInternal: boolean;
|
|
5
|
+
setIsInternal: (isInternal: boolean) => void;
|
|
6
|
+
}
|
|
7
|
+
declare const ComposerTabs: ({ canUseInternal, hintText, isInternal, setIsInternal, }: ComposerTabsProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export default ComposerTabs;
|
|
9
|
+
//# sourceMappingURL=ComposerTabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComposerTabs.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ComposerTabs.tsx"],"names":[],"mappings":"AAOA,UAAU,iBAAiB;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C;AAeD,QAAA,MAAM,YAAY,GAAI,0DAKnB,iBAAiB,mDA2CnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { Tabs } from "antd";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
const COMPOSER_TABS = [
|
|
8
|
+
{
|
|
9
|
+
isInternal: false,
|
|
10
|
+
activeClassName: "text-indigo-600",
|
|
11
|
+
labelKey: "reply",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
isInternal: true,
|
|
15
|
+
activeClassName: "text-orange-600",
|
|
16
|
+
labelKey: "internal",
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
const ComposerTabs = ({ canUseInternal, hintText, isInternal, setIsInternal, }) => {
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
const items = useMemo(() => COMPOSER_TABS.map((tab) => ({
|
|
22
|
+
key: String(tab.isInternal),
|
|
23
|
+
label: (_jsx("span", { className: clsx("text-sm font-semibold", isInternal === tab.isInternal
|
|
24
|
+
? tab.activeClassName
|
|
25
|
+
: "text-gray-500"), children: t(tab.labelKey) })),
|
|
26
|
+
children: null,
|
|
27
|
+
})), [isInternal, t]);
|
|
28
|
+
if (!canUseInternal) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return (_jsx(Tabs, { activeKey: String(isInternal), items: items, onChange: (value) => setIsInternal(value === "true"), tabBarExtraContent: {
|
|
32
|
+
left: _jsx("p", { className: "pr-4 text-sm text-gray-500", children: hintText }),
|
|
33
|
+
}, className: clsx("[&_.ant-tabs-content-holder]:hidden [&_.ant-tabs-ink-bar]:hidden [&_.ant-tabs-nav]:mb-0 [&_.ant-tabs-nav]:px-4 [&_.ant-tabs-nav]:pt-0 [&_.ant-tabs-nav]:before:border-b-0 [&_.ant-tabs-nav-wrap]:justify-end [&_.ant-tabs-extra-content]:flex [&_.ant-tabs-extra-content]:items-center [&_.ant-tabs-extra-content]:overflow-hidden [&_.ant-tabs-extra-content]:text-ellipsis [&_.ant-tabs-tab]:mb-0 [&_.ant-tabs-tab]:rounded-t-[16px] [&_.ant-tabs-tab]:border [&_.ant-tabs-tab]:border-b-0 [&_.ant-tabs-tab]:border-transparent [&_.ant-tabs-tab]:px-3 [&_.ant-tabs-tab]:py-1.5 [&_.ant-tabs-tab]:leading-5 [&_.ant-tabs-tab-btn]:leading-5", isInternal
|
|
34
|
+
? "[&_.ant-tabs-tab-active]:border-orange-200 [&_.ant-tabs-tab-active]:bg-orange-50"
|
|
35
|
+
: "[&_.ant-tabs-tab-active]:border-indigo-200 [&_.ant-tabs-tab-active]:bg-indigo-50") }));
|
|
36
|
+
};
|
|
37
|
+
export default ComposerTabs;
|