@droppii-org/chat-sdk 0.5.5 → 0.6.1
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/compose/ComposeNewMessageModal.js +3 -3
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationByGroupItem.js +4 -2
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +4 -2
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +15 -5
- package/dist/components/conversation-inbox/ConversationInboxList.js +3 -3
- package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.js +13 -11
- package/dist/components/conversation-inbox/contact-search/utils.d.ts +7 -1
- package/dist/components/conversation-inbox/contact-search/utils.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/utils.js +19 -15
- package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
- package/dist/components/message/GroupMembersDrawer.js +3 -3
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +27 -11
- package/dist/components/search-conversation/item/SearchItemAsGroup.js +3 -3
- package/dist/context/ChatContext.d.ts +1 -1
- package/dist/context/ChatContext.d.ts.map +1 -1
- package/dist/context/ChatContext.js +10 -1
- package/dist/hooks/conversation/useChatNavigate.d.ts +2 -0
- package/dist/hooks/conversation/useChatNavigate.d.ts.map +1 -0
- package/dist/hooks/conversation/useChatNavigate.js +14 -0
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +11 -4
- package/dist/hooks/group/useCreateGroup.js +3 -3
- package/dist/hooks/group/useDismissGroup.js +3 -3
- package/dist/hooks/group/useQuitGroup.js +3 -3
- package/dist/hooks/session/useConversationSessionState.d.ts +2 -1
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
- package/dist/hooks/session/useConversationSessionState.js +14 -5
- package/dist/hooks/session/useGetSession.d.ts +6 -0
- package/dist/hooks/session/useGetSession.d.ts.map +1 -1
- package/dist/hooks/session/useGetSession.js +17 -6
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +15 -2
- package/dist/screens/conversation-inbox/index.d.ts +1 -1
- package/dist/screens/conversation-inbox/index.d.ts.map +1 -1
- package/dist/screens/conversation-inbox/index.js +48 -14
- package/dist/store/conversation.d.ts +1 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +26 -18
- package/dist/store/type.d.ts +10 -0
- package/dist/store/type.d.ts.map +1 -1
- package/dist/types/chat.d.ts +2 -0
- package/dist/types/chat.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import { useEligibleAccounts } from "../../hooks/group/useEligibleAccounts";
|
|
|
10
10
|
import { DChatSDK } from "../../constants/sdk";
|
|
11
11
|
import useConversationStore from "../../store/conversation";
|
|
12
12
|
import CreateGroupForm from "../../components/group/CreateGroupForm";
|
|
13
|
-
import {
|
|
13
|
+
import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
14
14
|
const ComposeNewMessageModal = ({ open, onClose, }) => {
|
|
15
15
|
const { t } = useTranslation("conversation-inbox");
|
|
16
16
|
const [step, setStep] = useState("compose");
|
|
@@ -18,7 +18,7 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
|
|
|
18
18
|
const [openingUserID, setOpeningUserID] = useState(null);
|
|
19
19
|
const debouncedSearch = useDebounce(search, { wait: 300 });
|
|
20
20
|
const { accounts, isLoading } = useEligibleAccounts(debouncedSearch, open);
|
|
21
|
-
const
|
|
21
|
+
const onNavigate = useChatNavigate();
|
|
22
22
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
23
23
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
24
24
|
const handleClose = () => {
|
|
@@ -47,7 +47,7 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
|
|
|
47
47
|
newSearchParams.delete("userId");
|
|
48
48
|
newSearchParams.delete("groupId");
|
|
49
49
|
newSearchParams.delete("botId");
|
|
50
|
-
onNavigate
|
|
50
|
+
onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
|
|
51
51
|
handleClose();
|
|
52
52
|
}
|
|
53
53
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AASxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CAgE9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -3,13 +3,15 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import useConversationStore from "../../store/conversation";
|
|
5
5
|
import { useChatContext } from "../../context/ChatContext";
|
|
6
|
+
import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
6
7
|
import { formatTimestamp, parseLatestMessage } from "../../utils/common";
|
|
7
8
|
import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
|
|
8
9
|
import ThreadCard from "./ThreadCard";
|
|
9
10
|
import MentionIndicatorIcon from "./MentionIndicatorIcon";
|
|
10
11
|
const ConversationByGroupItem = ({ group, latestPreview, }) => {
|
|
11
12
|
const { t } = useTranslation();
|
|
12
|
-
const { user
|
|
13
|
+
const { user } = useChatContext();
|
|
14
|
+
const onNavigate = useChatNavigate();
|
|
13
15
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
14
16
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
15
17
|
const isSelected = useConversationStore((state) => state.selectedConversationId === group.conversationId);
|
|
@@ -22,7 +24,7 @@ const ConversationByGroupItem = ({ group, latestPreview, }) => {
|
|
|
22
24
|
newSearchParams.delete("userId");
|
|
23
25
|
newSearchParams.delete("groupId");
|
|
24
26
|
newSearchParams.delete("botId");
|
|
25
|
-
onNavigate
|
|
27
|
+
onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
|
|
26
28
|
};
|
|
27
29
|
const latestMsg = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsg) || "";
|
|
28
30
|
const latestMsgSendTime = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsgSendTime) || 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAYhD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDA+EhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
|
|
4
4
|
import useConversationStore from "../../store/conversation";
|
|
5
5
|
import { Avatar } from "antd";
|
|
6
6
|
import { useChatContext } from "../../context/ChatContext";
|
|
7
|
+
import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
7
8
|
import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
|
|
8
9
|
import { formatTimestamp, parseLatestMessage } from "../../utils/common";
|
|
9
10
|
import { isGroupSession } from "../../utils/imCommon";
|
|
@@ -13,7 +14,8 @@ import MentionIndicatorIcon from "./MentionIndicatorIcon";
|
|
|
13
14
|
const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
|
|
14
15
|
var _a;
|
|
15
16
|
const { t } = useTranslation();
|
|
16
|
-
const { user
|
|
17
|
+
const { user } = useChatContext();
|
|
18
|
+
const onNavigate = useChatNavigate();
|
|
17
19
|
const isSelected = useConversationStore((state) => {
|
|
18
20
|
var _a;
|
|
19
21
|
return state.selectedConversationId ===
|
|
@@ -29,7 +31,7 @@ const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
|
|
|
29
31
|
newSearchParams.delete("userId");
|
|
30
32
|
newSearchParams.delete("groupId");
|
|
31
33
|
newSearchParams.delete("botId");
|
|
32
|
-
onNavigate
|
|
34
|
+
onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
|
|
33
35
|
setSelectedConversationId(conversation.conversationID);
|
|
34
36
|
};
|
|
35
37
|
const { avatar, displayName = "" } = useConversationDisplayData(conversation || null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+
|
|
1
|
+
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+CAuVzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -31,13 +31,12 @@ const DeskConversationList = () => {
|
|
|
31
31
|
const searchParams = useSearchParams();
|
|
32
32
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
33
33
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
34
|
-
const conversationList = useConversationStore((state) => state.conversationList);
|
|
35
34
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
36
35
|
const filterSummary = useSessionStore((state) => state.filterSummary);
|
|
37
36
|
const viewMode = useSessionStore((state) => state.viewMode);
|
|
38
37
|
const isAllGroupsView = viewMode === "allGroups";
|
|
39
38
|
const { data: teamSummaries } = useGetTeamSessionSummary();
|
|
40
|
-
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, } = useGetSession({
|
|
39
|
+
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, isSyncing, } = useGetSession({
|
|
41
40
|
filter: filterSummary,
|
|
42
41
|
options: { isEnabled: !isAllGroupsView },
|
|
43
42
|
});
|
|
@@ -89,7 +88,13 @@ const DeskConversationList = () => {
|
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
90
|
setSelectedConversationId(threadId);
|
|
92
|
-
|
|
91
|
+
// Read the snapshot instead of subscribing — this effect must only
|
|
92
|
+
// re-run on threadId change. Depending on conversationList re-fired it
|
|
93
|
+
// on every unrelated mutation (including its own updateConversationList
|
|
94
|
+
// call below), looping the sidebar between loaded and loading.
|
|
95
|
+
const selectedConversation = useConversationStore
|
|
96
|
+
.getState()
|
|
97
|
+
.conversationList.find((conv) => conv.conversationID === threadId);
|
|
93
98
|
// threadId can come from a notification/stale search click — force a
|
|
94
99
|
// fresh membership check instead of trusting the groupID cache.
|
|
95
100
|
if (selectedConversation) {
|
|
@@ -110,7 +115,7 @@ const DeskConversationList = () => {
|
|
|
110
115
|
}
|
|
111
116
|
});
|
|
112
117
|
}
|
|
113
|
-
}, [searchParams
|
|
118
|
+
}, [searchParams]);
|
|
114
119
|
useEffect(() => {
|
|
115
120
|
const handler = (sessionUpdated) => {
|
|
116
121
|
if (sessionUpdated.status !== filterSummary.status ||
|
|
@@ -131,7 +136,12 @@ const DeskConversationList = () => {
|
|
|
131
136
|
}, 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 }) })) : (_jsx(Button, { "data-testid": "conversation-list-compose-icon", onClick: () => setIsComposeOpen(true), variant: "outlined", className: "p-0 w-[36px] h-[36px] shrink-0 text-gray-500", children: _jsx(Icon, { icon: "edit-b", size: 20 }) }))] }), _jsxs("div", { className: "flex items-center justify-between mt-2", children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate", children: pathName }), isAllGroupsView ? (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: "user-three-o", size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t("all_groups") })] })) : (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", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", style: {
|
|
132
137
|
height: "100%",
|
|
133
138
|
overflow: showSearch ? "hidden" : "auto",
|
|
134
|
-
}, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_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") }) })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }))) : (_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:
|
|
139
|
+
}, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_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") }) })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }))) : (_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: isSyncing ? (
|
|
140
|
+
// conversationList (the client cache dataFlatten joins
|
|
141
|
+
// against) hasn't caught up with this filter's BE response
|
|
142
|
+
// yet — showing the list now would flash a wrong, too-low
|
|
143
|
+
// count that corrects itself a moment later.
|
|
144
|
+
_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : (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, latestPreview: latestPreviewMap.get(session.conversationId) }, session.conversationId)))) })) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
|
|
135
145
|
body: {
|
|
136
146
|
padding: 0,
|
|
137
147
|
},
|
|
@@ -4,13 +4,13 @@ import { useTranslation } from "react-i18next";
|
|
|
4
4
|
import { Empty, Spin } from "antd";
|
|
5
5
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
6
6
|
import useConversationStore from "../../store/conversation";
|
|
7
|
-
import {
|
|
7
|
+
import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
8
8
|
import { Icon } from "../../components/icon";
|
|
9
9
|
import ConversationInboxItem from "./ConversationInboxItem";
|
|
10
10
|
const FooterSkeleton = () => (_jsxs("div", { className: "flex items-center gap-3 p-3", children: [_jsx("div", { className: "h-10 w-10 animate-pulse rounded-full bg-neutral-200" }), _jsxs("div", { className: "flex flex-1 flex-col gap-2", children: [_jsx("div", { className: "h-3 w-1/2 animate-pulse rounded bg-neutral-200" }), _jsx("div", { className: "h-2.5 w-1/3 animate-pulse rounded bg-neutral-200" })] })] }));
|
|
11
11
|
const ConversationInboxList = ({ items, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, onSelectThread, }) => {
|
|
12
12
|
const { t } = useTranslation("conversation-inbox");
|
|
13
|
-
const
|
|
13
|
+
const onNavigate = useChatNavigate();
|
|
14
14
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
15
15
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
16
16
|
const handleSelectThread = (item) => {
|
|
@@ -19,7 +19,7 @@ const ConversationInboxList = ({ items, isLoading, isFetchingNextPage, hasNextPa
|
|
|
19
19
|
newSearchParams.delete("userId");
|
|
20
20
|
newSearchParams.delete("groupId");
|
|
21
21
|
newSearchParams.delete("botId");
|
|
22
|
-
onNavigate
|
|
22
|
+
onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
|
|
23
23
|
setSelectedConversationId(item.conversationID);
|
|
24
24
|
setConversationData(item);
|
|
25
25
|
onSelectThread === null || onSelectThread === void 0 ? void 0 : onSelectThread(item);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContactSearchPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchPanel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ContactSearchPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchPanel.tsx"],"names":[],"mappings":"AA+BA,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,mBAAmB,uBAAuB,4CA2TrE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -6,9 +6,10 @@ import { useDebounce } from "ahooks";
|
|
|
6
6
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
7
7
|
import { useTranslation } from "react-i18next";
|
|
8
8
|
import { Icon } from "../../../components/icon";
|
|
9
|
-
import {
|
|
9
|
+
import { useChatNavigate } from "../../../hooks/conversation/useChatNavigate";
|
|
10
10
|
import { useSearchContacts } from "../../../hooks/search/useSearchContacts";
|
|
11
11
|
import { useSearchPublicGroups } from "../../../hooks/search/useSearchPublicGroups";
|
|
12
|
+
import useConversationStore from "../../../store/conversation";
|
|
12
13
|
import ContactSearchFilters from "./ContactSearchFilters";
|
|
13
14
|
import ContactSearchResultItem from "./ContactSearchResultItem";
|
|
14
15
|
import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
|
|
@@ -17,12 +18,13 @@ import ContactSearchTabs from "./ContactSearchTabs";
|
|
|
17
18
|
import ContactSearchViewMoreBar from "./ContactSearchViewMoreBar";
|
|
18
19
|
import PublicGroupResultItem from "./PublicGroupResultItem";
|
|
19
20
|
import { CONTACT_SEARCH_DEBOUNCE_MS, CONTACT_SEARCH_ICON_SIZE, CONTACT_SEARCH_PREVIEW_LIMIT, ContactSearchTab, } from "./constants";
|
|
20
|
-
import { buildContactSearchParams } from "./utils";
|
|
21
|
+
import { buildContactSearchParams, resolveContactSearchTarget } from "./utils";
|
|
21
22
|
const SCROLL_TARGET_ID = "contact-search-scroll";
|
|
22
23
|
const COMMUNITY_SCROLL_TARGET_ID = "contact-search-community-scroll";
|
|
23
24
|
const ContactSearchPanel = ({ open, onClose }) => {
|
|
24
25
|
const { t } = useTranslation("conversation-inbox");
|
|
25
|
-
const
|
|
26
|
+
const onNavigate = useChatNavigate();
|
|
27
|
+
const setPendingResolveTarget = useConversationStore((state) => state.setPendingResolveTarget);
|
|
26
28
|
const [keyword, setKeyword] = useState("");
|
|
27
29
|
const [activeType, setActiveType] = useState();
|
|
28
30
|
const [activeTab, setActiveTab] = useState(ContactSearchTab.All);
|
|
@@ -49,18 +51,18 @@ const ContactSearchPanel = ({ open, onClose }) => {
|
|
|
49
51
|
enabled: open && activeTab === ContactSearchTab.Community,
|
|
50
52
|
});
|
|
51
53
|
const handleOpenGroup = (groupID) => {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
params
|
|
55
|
-
params.delete("botId");
|
|
56
|
-
params.set("groupId", groupID);
|
|
54
|
+
const target = { groupId: groupID };
|
|
55
|
+
setPendingResolveTarget(target);
|
|
56
|
+
const params = buildContactSearchParams(target);
|
|
57
57
|
onClose();
|
|
58
|
-
onNavigate
|
|
58
|
+
onNavigate(`${window.location.pathname}?${params.toString()}`);
|
|
59
59
|
};
|
|
60
60
|
const handleSelectContact = (contact) => {
|
|
61
|
-
const
|
|
61
|
+
const target = resolveContactSearchTarget(contact);
|
|
62
|
+
setPendingResolveTarget(target);
|
|
63
|
+
const params = buildContactSearchParams(target);
|
|
62
64
|
onClose();
|
|
63
|
-
onNavigate
|
|
65
|
+
onNavigate(`${window.location.pathname}?${params.toString()}`);
|
|
64
66
|
};
|
|
65
67
|
// Filters belong to the `Liên hệ` tab only, so leaving it drops the active
|
|
66
68
|
// type — `Tất cả` always previews every contact type.
|
|
@@ -2,5 +2,11 @@ import { ContactSearchItem, ContactSearchType } from "../../../types/dto";
|
|
|
2
2
|
import { PeerType } from "../../../types/conversation";
|
|
3
3
|
export declare const resolveContactPeerType: (type: ContactSearchType) => PeerType;
|
|
4
4
|
export declare const formatLastOnlineTime: (lastOnlineTime: string | null) => string;
|
|
5
|
-
export
|
|
5
|
+
export interface ContactSearchTarget {
|
|
6
|
+
userId?: string;
|
|
7
|
+
groupId?: string;
|
|
8
|
+
botId?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const resolveContactSearchTarget: (contact: ContactSearchItem) => ContactSearchTarget;
|
|
11
|
+
export declare const buildContactSearchParams: (target: ContactSearchTarget) => URLSearchParams;
|
|
6
12
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAahD,eAAO,MAAM,sBAAsB,GAAI,MAAM,iBAAiB,KAAG,QAChC,CAAC;AAIlC,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,GAAG,IAAI,KAAG,MACd,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAahD,eAAO,MAAM,sBAAsB,GAAI,MAAM,iBAAiB,KAAG,QAChC,CAAC;AAIlC,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,GAAG,IAAI,KAAG,MACd,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,eAAO,MAAM,0BAA0B,GACrC,SAAS,iBAAiB,KACzB,mBAcF,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,mBAAmB,KAC1B,eAYF,CAAC"}
|
|
@@ -15,30 +15,34 @@ export const resolveContactPeerType = (type) => CONTACT_TYPE_TO_PEER_TYPE[type];
|
|
|
15
15
|
// The Vietnamese dayjs locale is already registered in the SDK layout, so
|
|
16
16
|
// fromNow() renders "1 giờ trước" without extra setup here.
|
|
17
17
|
export const formatLastOnlineTime = (lastOnlineTime) => lastOnlineTime ? dayjs(lastOnlineTime).fromNow() : "";
|
|
18
|
-
// Each contact type resolves to a different
|
|
18
|
+
// Each contact type resolves to a different target field; the conversation
|
|
19
19
|
// detail screen turns it into an existing thread or the new-chat flow.
|
|
20
|
-
|
|
20
|
+
// Shared by both the URL-synced and bubble-mode (store-only) navigation paths.
|
|
21
|
+
export const resolveContactSearchTarget = (contact) => {
|
|
21
22
|
var _a;
|
|
22
|
-
const params = new URLSearchParams(window.location.search);
|
|
23
|
-
params.delete("threadId");
|
|
24
|
-
params.delete("userId");
|
|
25
|
-
params.delete("groupId");
|
|
26
|
-
params.delete("botId");
|
|
27
23
|
if (contact.type === ContactSearchType.Group) {
|
|
28
|
-
|
|
29
|
-
params.set("groupId", contact.targetGroupId);
|
|
30
|
-
return params;
|
|
24
|
+
return contact.targetGroupId ? { groupId: contact.targetGroupId } : {};
|
|
31
25
|
}
|
|
32
26
|
// PDP bots carry their id in targetBotId; targetUserId is null for them.
|
|
33
27
|
if (contact.type === ContactSearchType.PdpBot) {
|
|
34
|
-
|
|
35
|
-
params.set("botId", contact.targetBotId);
|
|
36
|
-
return params;
|
|
28
|
+
return contact.targetBotId ? { botId: contact.targetBotId } : {};
|
|
37
29
|
}
|
|
38
30
|
// Individual contacts: sellers carry targetUserId, customers carry
|
|
39
31
|
// targetCustomerId — the other field is null in each case.
|
|
40
32
|
const peerId = (_a = contact.targetUserId) !== null && _a !== void 0 ? _a : contact.targetCustomerId;
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
return peerId ? { userId: peerId } : {};
|
|
34
|
+
};
|
|
35
|
+
export const buildContactSearchParams = (target) => {
|
|
36
|
+
const params = new URLSearchParams(window.location.search);
|
|
37
|
+
params.delete("threadId");
|
|
38
|
+
params.delete("userId");
|
|
39
|
+
params.delete("groupId");
|
|
40
|
+
params.delete("botId");
|
|
41
|
+
if (target.groupId)
|
|
42
|
+
params.set("groupId", target.groupId);
|
|
43
|
+
if (target.botId)
|
|
44
|
+
params.set("botId", target.botId);
|
|
45
|
+
if (target.userId)
|
|
46
|
+
params.set("userId", target.userId);
|
|
43
47
|
return params;
|
|
44
48
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,UAAU,uBAAuB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA0CD,QAAA,MAAM,kBAAkB,GAAI,wDAKzB,uBAAuB,4CAwjBzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -5,7 +5,7 @@ import ConfirmModal from "../../components/common/ConfirmModal";
|
|
|
5
5
|
import { useEffect, useMemo, useState } from "react";
|
|
6
6
|
import { useDebounce } from "ahooks";
|
|
7
7
|
import { useTranslation } from "react-i18next";
|
|
8
|
-
import {
|
|
8
|
+
import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
9
9
|
import useAuthStore from "../../store/auth";
|
|
10
10
|
import useConversationStore from "../../store/conversation";
|
|
11
11
|
import { useGroupMembers } from "../../hooks/group/useGroupMembers";
|
|
@@ -34,7 +34,7 @@ const getMemberRoleLabel = ({ member, customerUserID, t, }) => {
|
|
|
34
34
|
const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled = false, }) => {
|
|
35
35
|
var _a, _b;
|
|
36
36
|
const { t } = useTranslation();
|
|
37
|
-
const
|
|
37
|
+
const onNavigate = useChatNavigate();
|
|
38
38
|
const [isOpen, setIsOpen] = useState(false);
|
|
39
39
|
const [showLeaveConfirm, setShowLeaveConfirm] = useState(false);
|
|
40
40
|
const [showAddMember, setShowAddMember] = useState(false);
|
|
@@ -244,7 +244,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
244
244
|
try {
|
|
245
245
|
await leaveConversation({ conversationId });
|
|
246
246
|
message.success(t("leave_group_success"));
|
|
247
|
-
onNavigate
|
|
247
|
+
onNavigate(window.location.pathname);
|
|
248
248
|
setShowLeaveConfirm(false);
|
|
249
249
|
setIsOpen(false);
|
|
250
250
|
}
|
|
@@ -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":"AAiCA,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;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAkV3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -24,6 +24,7 @@ import { useJoinGroupFlow } from "../../hooks/session/useJoinGroupFlow";
|
|
|
24
24
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
25
25
|
import { isGroupConversation } from "../../utils/imCommon";
|
|
26
26
|
import { ConnectStatus, SessionStatus } from "../../types/chat";
|
|
27
|
+
import { PeerType } from "../../types/conversation";
|
|
27
28
|
import PinLimitModal from "../../components/conversation-detail/PinLimitModal";
|
|
28
29
|
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
29
30
|
dayjs.extend(isToday);
|
|
@@ -34,29 +35,36 @@ const MessageList = (props) => {
|
|
|
34
35
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
35
36
|
const permissions = useGroupPermission(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID);
|
|
36
37
|
const isGroupChat = isGroupConversation(conversationData !== null && conversationData !== void 0 ? conversationData : undefined);
|
|
38
|
+
// A CX/support session can ride a Group session with peerType missing on
|
|
39
|
+
// the instant response (see isGroupConversation) — non-member/mute checks
|
|
40
|
+
// must not fire off that loose signal, or a support thread with no group
|
|
41
|
+
// membership record gets wrongly treated as a blocked group member.
|
|
42
|
+
const isConfirmedGroup = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.GROUP;
|
|
37
43
|
const groupID = conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID;
|
|
38
44
|
const currentGroupInfo = useConversationStore((state) => groupID ? state.currentGroupInfo.get(groupID) : undefined);
|
|
39
45
|
const currentMemberInGroup = useConversationStore((state) => groupID ? state.currentMemberInGroup.get(groupID) : undefined);
|
|
40
46
|
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
47
|
+
const setIsViewOnlyMember = useConversationStore((state) => state.setIsViewOnlyMember);
|
|
41
48
|
// "Fresh" now means "we have a cache entry for this groupID" — populated
|
|
42
49
|
// either by a fetch just now or a cache hit from earlier in the session,
|
|
43
50
|
// not necessarily a response that JUST arrived (that's the point of the
|
|
44
51
|
// groupID cache: no spinner on reopening a previously-viewed group).
|
|
45
52
|
const isGroupInfoFresh = currentGroupInfo !== undefined;
|
|
46
|
-
const isGroupStateFresh = !
|
|
53
|
+
const isGroupStateFresh = !isConfirmedGroup ||
|
|
47
54
|
(isGroupInfoFresh &&
|
|
48
55
|
(!currentMemberInGroup || currentMemberInGroup.roleLevel !== undefined));
|
|
49
56
|
// Group-wide mute (GroupThreadInfo.tsx) restricts Normal members only.
|
|
50
|
-
const isRestrictedByGroupMute =
|
|
57
|
+
const isRestrictedByGroupMute = isConfirmedGroup &&
|
|
51
58
|
(currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status) === GroupStatus.Muted &&
|
|
52
59
|
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Normal;
|
|
53
|
-
// useGroupPermission defaults to Normal-role
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
const
|
|
60
|
+
// useGroupPermission defaults to Normal-role when currentMemberInGroup
|
|
61
|
+
// is undefined — wrong once absent for good, not just not-loaded-yet.
|
|
62
|
+
// Gated on isConfirmedGroup: a support thread has no membership record
|
|
63
|
+
// by design, not because it's a removed member.
|
|
64
|
+
const isConfirmedNonMember = isConfirmedGroup && isGroupInfoFresh && !currentMemberInGroup;
|
|
65
|
+
const canSendMessage = !isConfirmedGroup ||
|
|
58
66
|
(!isConfirmedNonMember && permissions.sendMessage && !isRestrictedByGroupMute);
|
|
59
|
-
const canPinMessage = !
|
|
67
|
+
const canPinMessage = !isConfirmedGroup || (!isConfirmedNonMember && permissions.pinMessage);
|
|
60
68
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
61
69
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
62
70
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
@@ -71,16 +79,24 @@ const MessageList = (props) => {
|
|
|
71
79
|
const pinnedTotalCount = useConversationStore((state) => state.pinnedTotalCount);
|
|
72
80
|
const pinnedRemainingCount = useConversationStore((state) => state.pinnedRemainingCount);
|
|
73
81
|
const [pinLimitModalOpen, setPinLimitModalOpen] = useState(false);
|
|
74
|
-
const {
|
|
82
|
+
const { rawGroupId, latestConversationSession, refetchConversationSessions, } = useConversationSessionState({
|
|
75
83
|
conversationId,
|
|
76
84
|
conversationType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType,
|
|
77
85
|
peerType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType,
|
|
78
86
|
groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
|
|
79
87
|
});
|
|
80
88
|
const { isJoined, isCheckingMembership, shouldRestrictToLastSession, joinGroup, isJoining, } = useJoinGroupFlow({
|
|
81
|
-
groupId,
|
|
89
|
+
groupId: rawGroupId,
|
|
82
90
|
sessionId: latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.id,
|
|
83
91
|
});
|
|
92
|
+
// A stale isViewOnlyMember=true only ever clears via the WASM join-group
|
|
93
|
+
// path or a WS event — neither fires for a support thread joined via
|
|
94
|
+
// useJoinSession's REST call. Sync it off once isJoined confirms true.
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!isConfirmedGroup && isJoined && isViewOnlyMember) {
|
|
97
|
+
setIsViewOnlyMember(false);
|
|
98
|
+
}
|
|
99
|
+
}, [isConfirmedGroup, isJoined, isViewOnlyMember, setIsViewOnlyMember]);
|
|
84
100
|
const messageController = useMessage(conversationId, searchClientMsgID);
|
|
85
101
|
const { loadState, moreOldLoading, moreNewLoading } = messageController;
|
|
86
102
|
const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: user === null || user === void 0 ? void 0 : user.userID, isGroupChat }, messageController));
|
|
@@ -164,7 +180,7 @@ const MessageList = (props) => {
|
|
|
164
180
|
if (!conversationData) {
|
|
165
181
|
return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
|
|
166
182
|
}
|
|
167
|
-
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), (
|
|
183
|
+
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), (Boolean(rawGroupId) && isCheckingMembership) || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : !isConfirmedGroup && shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", "data-cp-name": "JoinGroupRequired", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : isViewOnlyMember ? (
|
|
168
184
|
// Brief fallback while isJoined catches up after the kick event.
|
|
169
185
|
_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t(isRestrictedByGroupMute ? "send_message_disabled_group_mute" : "send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(ConfirmModal, { open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, children: _jsx("p", { children: t("revoke_message_confirm_message") }) }), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
|
|
170
186
|
};
|
|
@@ -3,14 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { GroupVisibility } from "@droppii-org/wasm-client-sdk";
|
|
5
5
|
import useConversationStore from "../../../store/conversation";
|
|
6
|
-
import {
|
|
6
|
+
import { useChatNavigate } from "../../../hooks/conversation/useChatNavigate";
|
|
7
7
|
import { highlightSearch, sanitizeHtml } from "../../../utils/common";
|
|
8
8
|
import { Icon } from "../../../components/icon";
|
|
9
9
|
import GroupCompositeAvatar from "../../../components/group/GroupCompositeAvatar";
|
|
10
10
|
const SearchItemAsGroup = (props) => {
|
|
11
11
|
const { group, searchTerm = "" } = props;
|
|
12
12
|
const { t } = useTranslation();
|
|
13
|
-
const
|
|
13
|
+
const onNavigate = useChatNavigate();
|
|
14
14
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
15
15
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
16
16
|
const { conversation } = group;
|
|
@@ -24,7 +24,7 @@ const SearchItemAsGroup = (props) => {
|
|
|
24
24
|
newSearchParams.delete("userId");
|
|
25
25
|
newSearchParams.delete("groupId");
|
|
26
26
|
newSearchParams.delete("botId");
|
|
27
|
-
onNavigate
|
|
27
|
+
onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
|
|
28
28
|
};
|
|
29
29
|
return (_jsxs("div", { className: "py-3 px-2 flex items-center gap-3 hover:bg-gray-100 hover:rounded-sm cursor-pointer border-b mx-1", onClick: onPressItem, "data-cp-name": "SearchItemAsGroup", children: [_jsx(GroupCompositeAvatar, { groupID: group.groupId, faceURL: conversation.faceURL, groupName: conversation.showName, size: 48 }), _jsxs("div", { className: "flex flex-col flex-1 min-w-0 gap-0.5", children: [_jsx("p", { className: "text-sm font-semibold truncate", dangerouslySetInnerHTML: {
|
|
30
30
|
__html: sanitizeHtml(highlightSearch(conversation.showName || "", searchTerm)),
|
|
@@ -2,5 +2,5 @@ import "../styles/global.css";
|
|
|
2
2
|
import { ChatContextType, ChatProviderProps } from "../types/chat";
|
|
3
3
|
export declare const ChatContext: import("react").Context<ChatContextType>;
|
|
4
4
|
export declare const useChatContext: () => ChatContextType;
|
|
5
|
-
export declare const ChatProvider: ({ children, config, renderCustomMessage, onNavigate, renderGroupAccessModal, theme, }: ChatProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare const ChatProvider: ({ children, config, renderCustomMessage, onNavigate, useUrlState, renderGroupAccessModal, theme, }: ChatProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
//# sourceMappingURL=ChatContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,iBAAiB,EAGlB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,iBAAiB,EAGlB,MAAM,cAAc,CAAC;AAUtB,eAAO,MAAM,WAAW,0CAQtB,CAAC;AAEH,eAAO,MAAM,cAAc,uBAAgC,CAAC;AAS5D,eAAO,MAAM,YAAY,GAAI,oGAQ1B,iBAAiB,4CAiInB,CAAC"}
|
|
@@ -6,6 +6,7 @@ import { ConnectStatus, SyncStatus, } from "../types/chat";
|
|
|
6
6
|
import { DChatSDK } from "../constants/sdk";
|
|
7
7
|
import MainLayout from "../layout";
|
|
8
8
|
import useAuthStore from "../store/auth";
|
|
9
|
+
import { setUrlStateEnabled } from "../store/conversation";
|
|
9
10
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
10
11
|
import { ConfigProvider } from "antd";
|
|
11
12
|
const queryClient = new QueryClient();
|
|
@@ -16,6 +17,7 @@ export const ChatContext = createContext({
|
|
|
16
17
|
getSelfUserInfo: () => { },
|
|
17
18
|
updateConnectStatus: () => { },
|
|
18
19
|
updateSyncStatus: () => { },
|
|
20
|
+
useUrlState: true,
|
|
19
21
|
});
|
|
20
22
|
export const useChatContext = () => useContext(ChatContext);
|
|
21
23
|
const DEFAULT_THEME = {
|
|
@@ -24,8 +26,14 @@ const DEFAULT_THEME = {
|
|
|
24
26
|
"--dc-color-primary-light": "#E6E9FF",
|
|
25
27
|
"--dc-color-primary-focus": "#607CFB",
|
|
26
28
|
};
|
|
27
|
-
export const ChatProvider = ({ children, config, renderCustomMessage, onNavigate, renderGroupAccessModal, theme, }) => {
|
|
29
|
+
export const ChatProvider = ({ children, config, renderCustomMessage, onNavigate, useUrlState = true, renderGroupAccessModal, theme, }) => {
|
|
28
30
|
var _a, _b, _c, _d;
|
|
31
|
+
// The store's clearSelectedConversation touches window.history directly
|
|
32
|
+
// (it has no React context access) — mirror the flag there so bubble mode
|
|
33
|
+
// doesn't clobber the host page's own URL.
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setUrlStateEnabled(useUrlState);
|
|
36
|
+
}, [useUrlState]);
|
|
29
37
|
const themeStyle = {
|
|
30
38
|
"--dc-color-primary": (_a = theme === null || theme === void 0 ? void 0 : theme.primary) !== null && _a !== void 0 ? _a : DEFAULT_THEME["--dc-color-primary"],
|
|
31
39
|
"--dc-color-primary-hover": (_b = theme === null || theme === void 0 ? void 0 : theme.primaryHover) !== null && _b !== void 0 ? _b : DEFAULT_THEME["--dc-color-primary-hover"],
|
|
@@ -109,6 +117,7 @@ export const ChatProvider = ({ children, config, renderCustomMessage, onNavigate
|
|
|
109
117
|
updateSyncStatus,
|
|
110
118
|
renderCustomMessage,
|
|
111
119
|
onNavigate,
|
|
120
|
+
useUrlState,
|
|
112
121
|
renderGroupAccessModal,
|
|
113
122
|
}, children: _jsx(ConfigProvider, { getPopupContainer: () => { var _a; return (_a = rootRef.current) !== null && _a !== void 0 ? _a : document.body; }, children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx("div", { ref: rootRef, style: themeStyle, className: "contents", children: _jsx(MainLayout, { children: children }) }) }) }) }));
|
|
114
123
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChatNavigate.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useChatNavigate.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,eAAe,cAIlB,MAAM,SAMf,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
4
|
+
// Every call site builds a URL and hands it to onNavigate — wrapping that
|
|
5
|
+
// here means bubble-mode hosts (useUrlState: false) get a single place that
|
|
6
|
+
// no-ops instead of needing every call site to check the flag itself.
|
|
7
|
+
export const useChatNavigate = () => {
|
|
8
|
+
const { onNavigate, useUrlState = true } = useChatContext();
|
|
9
|
+
return useCallback((url) => {
|
|
10
|
+
if (!useUrlState)
|
|
11
|
+
return;
|
|
12
|
+
onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(url);
|
|
13
|
+
}, [onNavigate, useUrlState]);
|
|
14
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,cAAc,YA8iB1B,CAAC"}
|