@droppii-org/chat-sdk 0.7.5 → 0.7.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/conversation/DeskBackgroundSyncStatus.d.ts +4 -0
- package/dist/components/conversation/DeskBackgroundSyncStatus.d.ts.map +1 -0
- package/dist/components/conversation/DeskBackgroundSyncStatus.js +17 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +13 -3
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +3 -6
- package/dist/components/conversation-detail/PinnedMessagesPanel.d.ts.map +1 -1
- package/dist/components/conversation-detail/PinnedMessagesPanel.js +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
- package/dist/components/conversation-inbox/contact-search/ContactSearchCommunitySection.d.ts +10 -0
- package/dist/components/conversation-inbox/contact-search/ContactSearchCommunitySection.d.ts.map +1 -0
- package/dist/components/conversation-inbox/contact-search/ContactSearchCommunitySection.js +15 -0
- package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.js +39 -28
- package/dist/components/conversation-inbox/contact-search/constants.js +2 -2
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +8 -6
- package/dist/hooks/conversation/useResolveConversation.d.ts.map +1 -1
- package/dist/hooks/conversation/useResolveConversation.js +2 -5
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +2 -0
- package/dist/hooks/init/useCanLoadChatData.d.ts +7 -0
- package/dist/hooks/init/useCanLoadChatData.d.ts.map +1 -0
- package/dist/hooks/init/useCanLoadChatData.js +15 -0
- package/dist/hooks/message/useCanPinMessage.d.ts +2 -0
- package/dist/hooks/message/useCanPinMessage.d.ts.map +1 -0
- package/dist/hooks/message/useCanPinMessage.js +29 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +3 -5
- package/dist/hooks/message/useUnreadMessageIndicator.d.ts.map +1 -1
- package/dist/hooks/message/useUnreadMessageIndicator.js +3 -5
- package/dist/hooks/search/useSearchPublicGroups.d.ts +1 -0
- package/dist/hooks/search/useSearchPublicGroups.d.ts.map +1 -1
- package/dist/hooks/search/useSearchPublicGroups.js +6 -0
- package/dist/hooks/session/useAgentSupportThreadJoin.d.ts +1 -0
- package/dist/hooks/session/useAgentSupportThreadJoin.d.ts.map +1 -1
- package/dist/hooks/session/useAgentSupportThreadJoin.js +13 -2
- package/dist/hooks/session/useSessionLatestPreview.d.ts +4 -1
- package/dist/hooks/session/useSessionLatestPreview.d.ts.map +1 -1
- package/dist/hooks/session/useSessionLatestPreview.js +4 -1
- package/dist/locales/vi/boot.json +3 -2
- package/dist/locales/vi/common.json +1 -0
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +3 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts +3 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts.map +1 -1
- package/dist/screens/desk-message/DeskWorkspace.js +4 -4
- package/dist/screens/desk-message/index.d.ts +2 -0
- package/dist/screens/desk-message/index.d.ts.map +1 -1
- package/dist/screens/desk-message/index.js +26 -8
- package/dist/store/boot.d.ts +15 -0
- package/dist/store/boot.d.ts.map +1 -1
- package/dist/store/boot.js +20 -2
- package/dist/styles/global.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Under the list header while a fresh device's first sync keeps pulling the other conversations. */
|
|
2
|
+
declare const DeskBackgroundSyncStatus: () => import("react/jsx-runtime").JSX.Element | null;
|
|
3
|
+
export default DeskBackgroundSyncStatus;
|
|
4
|
+
//# sourceMappingURL=DeskBackgroundSyncStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeskBackgroundSyncStatus.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskBackgroundSyncStatus.tsx"],"names":[],"mappings":"AAQA,qGAAqG;AACrG,QAAA,MAAM,wBAAwB,sDA+B7B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import BootProgressBar from "../../components/boot/BootProgressBar";
|
|
5
|
+
import useBootStore, { CONVERSATIONS_SYNCED_PROGRESS, selectIsSyncingInBackground, } from "../../store/boot";
|
|
6
|
+
/** Under the list header while a fresh device's first sync keeps pulling the other conversations. */
|
|
7
|
+
const DeskBackgroundSyncStatus = () => {
|
|
8
|
+
const { t } = useTranslation("boot");
|
|
9
|
+
const isSyncing = useBootStore(selectIsSyncingInBackground);
|
|
10
|
+
const progress = useBootStore((state) => state.syncProgress);
|
|
11
|
+
if (!isSyncing)
|
|
12
|
+
return null;
|
|
13
|
+
// Core reports exactly 10 when the conversations are stored; only later values say much.
|
|
14
|
+
const showPercent = progress > CONVERSATIONS_SYNCED_PROGRESS;
|
|
15
|
+
return (_jsxs("div", { "data-cp-name": "DeskBackgroundSyncStatus", className: "flex flex-col gap-1.5 border-b border-gray-25 px-4 py-2", children: [_jsxs("p", { role: "status", className: "m-0 flex items-center gap-2 text-xs text-gray-600", children: [_jsx("span", { "aria-hidden": "true", className: "h-3 w-3 shrink-0 rounded-full border-2 border-primary-25 border-t-primary-400 motion-safe:animate-spin" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: t("desk.background_sync") }), showPercent && (_jsxs("span", { className: "font-semibold tabular-nums text-primary-400", children: [progress, "%"] }))] }), _jsx(BootProgressBar, { mode: showPercent ? "progress" : "indeterminate", value: progress, label: t("sync_progress_label") })] }));
|
|
16
|
+
};
|
|
17
|
+
export default DeskBackgroundSyncStatus;
|
|
@@ -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":"AAiCA,UAAU,yBAAyB;IACjC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CACtD;AAED,QAAA,MAAM,oBAAoB,GAAI,mBAAmB,yBAAyB,4CA2RzE,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -20,10 +20,12 @@ import { DChatSDK } from "../../constants/sdk";
|
|
|
20
20
|
import emitter from "../../utils/events";
|
|
21
21
|
import { useGetSessionSummary } from "../../hooks/session/useGetSessionSummary";
|
|
22
22
|
import { getActiveFolder } from "../../store/sessionFolder";
|
|
23
|
+
import useBootStore from "../../store/boot";
|
|
23
24
|
import { ALL_GROUPS_KEY } from "../../components/session/sessionFilters";
|
|
24
25
|
import DeskListHeader from "./DeskListHeader";
|
|
25
26
|
import DeskListEmptyState from "./DeskListEmptyState";
|
|
26
27
|
import DeskListSkeleton from "./DeskListSkeleton";
|
|
28
|
+
import DeskBackgroundSyncStatus from "./DeskBackgroundSyncStatus";
|
|
27
29
|
import { getListHeading } from "./listHeading";
|
|
28
30
|
import ComposeNewMessageModal from "../../components/compose/ComposeNewMessageModal";
|
|
29
31
|
import { useCloseOnSidePanelOpen } from "../../hooks/common/useCloseOnSidePanelOpen";
|
|
@@ -46,9 +48,9 @@ const DeskConversationList = ({ onOpenAppMenu }) => {
|
|
|
46
48
|
options: { isEnabled: !isAllGroupsView },
|
|
47
49
|
});
|
|
48
50
|
const { groups: conversationGroups, fetchNextPage: fetchNextGroupsPage, hasNextPage: hasNextGroupsPage, isLoading: isConversationGroupsLoading, } = useConversationGroups({ enabled: isAllGroupsView });
|
|
49
|
-
const latestPreviewMap = useSessionLatestPreview(rawSessions);
|
|
51
|
+
const { previews: latestPreviewMap, isLoading: isSessionPreviewLoading } = useSessionLatestPreview(rawSessions);
|
|
50
52
|
const sessions = useSortedSessions(rawSessions, latestPreviewMap);
|
|
51
|
-
const groupLatestPreviewMap = useSessionLatestPreview(conversationGroups);
|
|
53
|
+
const { previews: groupLatestPreviewMap, isLoading: isGroupPreviewLoading } = useSessionLatestPreview(conversationGroups);
|
|
52
54
|
const { data: mySummary } = useGetSessionSummary();
|
|
53
55
|
const folder = getActiveFolder(filterSummary, viewMode);
|
|
54
56
|
const heading = getListHeading({ folder, mySummary, teams: teamSummaries });
|
|
@@ -56,6 +58,14 @@ const DeskConversationList = ({ onOpenAppMenu }) => {
|
|
|
56
58
|
const isListLoading = isAllGroupsView
|
|
57
59
|
? isConversationGroupsLoading
|
|
58
60
|
: isSessionsLoading || isSyncing;
|
|
61
|
+
// The desk keeps its boot screen until the rows on screen have their conversation and preview.
|
|
62
|
+
const isFirstScreenLoaded = isAllGroupsView
|
|
63
|
+
? !isConversationGroupsLoading && !isGroupPreviewLoading
|
|
64
|
+
: !isSessionsLoading && !isSyncing && !isSessionPreviewLoading;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (isFirstScreenLoaded)
|
|
67
|
+
useBootStore.getState().markFirstScreenReady();
|
|
68
|
+
}, [isFirstScreenLoaded]);
|
|
59
69
|
const debouncedSearch = useDebounce(search, { wait: 500 });
|
|
60
70
|
// Closing hands focus back to the field (drawer focus restore, Esc); that focus
|
|
61
71
|
// must not reopen the search it just closed.
|
|
@@ -150,7 +160,7 @@ const DeskConversationList = ({ onOpenAppMenu }) => {
|
|
|
150
160
|
if (!showSearch && value)
|
|
151
161
|
setShowSearchTrue();
|
|
152
162
|
setSearch(value);
|
|
153
|
-
}, onSearchFocus: onSearchFocus, onCloseSearch: onCloseSearch, onCompose: () => setIsComposeOpen(true), onOpenAppMenu: onOpenAppMenu }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", "aria-busy": isListLoading, style: {
|
|
163
|
+
}, onSearchFocus: onSearchFocus, onCloseSearch: onCloseSearch, onCompose: () => setIsComposeOpen(true), onOpenAppMenu: onOpenAppMenu }), _jsx(DeskBackgroundSyncStatus, {}), _jsxs("div", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", "aria-busy": isListLoading, style: {
|
|
154
164
|
height: "100%",
|
|
155
165
|
overflow: showSearch ? "hidden" : "auto",
|
|
156
166
|
}, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx(DeskListSkeleton, { rows: 8 })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_jsx(DeskListEmptyState, { filterKey: emptyFilterKey })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx(DeskListSkeleton, { rows: 2 }), scrollableTarget: "scrollableConversationsDiv", children: _jsx("div", { className: "flex flex-col gap-0.5 px-1 pb-2", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }) }))) : isSessionsLoading || (isSyncing && !(sessions === null || sessions === void 0 ? void 0 : sessions.length)) ? (
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,4DAI9B,4BAA4B,
|
|
1
|
+
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,4DAI9B,4BAA4B,4CAkS9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -11,7 +11,7 @@ import { useMessageListScroll } from "../../hooks/message/useMessageListScroll";
|
|
|
11
11
|
import { useUnreadMessageIndicator } from "../../hooks/message/useUnreadMessageIndicator";
|
|
12
12
|
import { usePinMessage } from "../../hooks/message/usePinMessage";
|
|
13
13
|
import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
|
|
14
|
-
import {
|
|
14
|
+
import { useCanPinMessage } from "../../hooks/message/useCanPinMessage";
|
|
15
15
|
import { useViewOnlyGroupRefresh } from "../../hooks/conversation/useViewOnlyGroupRefresh";
|
|
16
16
|
import { useConsultantChatNavigation } from "../../hooks/conversation/useConsultantChatNavigation";
|
|
17
17
|
import useConversationStore from "../../store/conversation";
|
|
@@ -27,7 +27,7 @@ import PinLimitModal from "./PinLimitModal";
|
|
|
27
27
|
const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessageRevoked, }) => {
|
|
28
28
|
const { t } = useTranslation("conversation-inbox");
|
|
29
29
|
const { t: tCommon } = useTranslation();
|
|
30
|
-
const { user
|
|
30
|
+
const { user } = useChatContext();
|
|
31
31
|
const authUserID = useAuthStore((state) => state.userID);
|
|
32
32
|
const conversation = useConversationStore((state) => state.conversationData);
|
|
33
33
|
const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
|
|
@@ -37,10 +37,7 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
|
|
|
37
37
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
38
38
|
const conversationId = selectedConversationId || (conversation === null || conversation === void 0 ? void 0 : conversation.conversationID) || "";
|
|
39
39
|
const isGroupChat = conversation ? resolveIsGroupChat(conversation) : false;
|
|
40
|
-
const
|
|
41
|
-
// Some hosts run pin without their backend returning PIN_MESSAGE yet, so
|
|
42
|
-
// the permission check would hide a feature that is live for them.
|
|
43
|
-
const canPinMessage = !isGroupChat || permissions.pinMessage || allowPinMessageWithoutPermission;
|
|
40
|
+
const canPinMessage = useCanPinMessage();
|
|
44
41
|
const viewerUserID = (user === null || user === void 0 ? void 0 : user.userID) || authUserID;
|
|
45
42
|
const isAgentView = isSupportSessionAgentView(Object.assign(Object.assign({}, conversation), { conversationID: conversationId }), viewerUserID);
|
|
46
43
|
// App history strips the assignment log and session dividers, which an
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PinnedMessagesPanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/PinnedMessagesPanel.tsx"],"names":[],"mappings":"AAYA,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,mBAAmB,GAAI,8BAI1B,wBAAwB,
|
|
1
|
+
{"version":3,"file":"PinnedMessagesPanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/PinnedMessagesPanel.tsx"],"names":[],"mappings":"AAYA,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,mBAAmB,GAAI,8BAI1B,wBAAwB,4CAoH1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -57,6 +57,6 @@ const PinnedMessagesPanel = ({ open, onClose, canUnpin = true, }) => {
|
|
|
57
57
|
borderTopRightRadius: 16,
|
|
58
58
|
overflow: "hidden",
|
|
59
59
|
},
|
|
60
|
-
}, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-lg font-bold text-gray-900", children: t("pinned_panel_title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("pinned_panel_title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto py-3", children: [_jsx("p", { className: "px-3 text-sm leading-[160%] text-gray-600", children: t("pinned_panel_hint") }), pinnedMessageList.map((message) => (_jsx(PinnedMessageItem, { message: message, contextMenuOpen: openContextMenuId === message.clientMsgID, onContextMenuOpenChange: (isOpen) => setOpenContextMenuId(isOpen ? message.clientMsgID : null), onUnpin: handleUnpin, onJump: handleJump, canUnpin: canUnpin }, message.clientMsgID)))] }, openCount)] }) }));
|
|
60
|
+
}, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-lg font-bold text-gray-900", children: t("pinned_panel_title") }), _jsx(Button, { type: "text", shape: "default", className: "!h-6 !w-6 !p-0 text-gray-900", onClick: onClose, "aria-label": t("pinned_panel_title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto py-3", children: [_jsx("p", { className: "px-3 text-sm leading-[160%] text-gray-600", children: t(canUnpin ? "pinned_panel_hint" : "pinned_panel_hint_view_only") }), pinnedMessageList.map((message) => (_jsx(PinnedMessageItem, { message: message, contextMenuOpen: openContextMenuId === message.clientMsgID, onContextMenuOpenChange: (isOpen) => setOpenContextMenuId(isOpen ? message.clientMsgID : null), onUnpin: handleUnpin, onJump: handleJump, canUnpin: canUnpin }, message.clientMsgID)))] }, openCount)] }) }));
|
|
61
61
|
};
|
|
62
62
|
export default PinnedMessagesPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BotMenuSheet.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotMenuSheet.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CAC3C;AAED,QAAA,MAAM,YAAY,GAAI,+CAMnB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"BotMenuSheet.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotMenuSheet.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CAC3C;AAED,QAAA,MAAM,YAAY,GAAI,+CAMnB,iBAAiB,4CAmEnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -17,7 +17,7 @@ const BotMenuSheet = ({ open, items, disabled, onClose, onSelect, }) => {
|
|
|
17
17
|
body: { padding: "16px 16px 32px" },
|
|
18
18
|
mask: { backgroundColor: "rgba(0, 0, 0, 0.8)" },
|
|
19
19
|
content: { background: "transparent", boxShadow: "none" },
|
|
20
|
-
}, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex flex-col overflow-hidden rounded-2xl bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("thread_detail.bot.menu.title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("thread_detail.bot.menu.title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsx("div", { className: "flex flex-col", children: items.map((item, index) => (_jsxs("button", { type: "button", disabled: disabled, onClick: () => handleSelect(item), className: "flex items-center gap-4 border-b border-gray-25 px-3 py-3 text-left transition-colors last:border-b-0 disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx("span", { className: "flex-1 text-[16px] font-medium leading-[1.6] text-gray-900", children: item.id === BOT_DEFAULT_BUTTON_ID
|
|
20
|
+
}, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex flex-col overflow-hidden rounded-2xl bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("thread_detail.bot.menu.title") }), _jsx(Button, { type: "text", shape: "default", className: "!h-6 !w-6 !p-0 text-gray-900", onClick: onClose, "aria-label": t("thread_detail.bot.menu.title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsx("div", { className: "flex flex-col", children: items.map((item, index) => (_jsxs("button", { type: "button", disabled: disabled, onClick: () => handleSelect(item), className: "flex items-center gap-4 border-b border-gray-25 px-3 py-3 text-left transition-colors last:border-b-0 disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx("span", { className: "flex-1 text-[16px] font-medium leading-[1.6] text-gray-900", children: item.id === BOT_DEFAULT_BUTTON_ID
|
|
21
21
|
? t("thread_detail.bot.restart")
|
|
22
22
|
: item.label }), _jsx(Icon, { icon: "angle-right-o", size: CHEVRON_SIZE, className: "text-gray-900" })] }, `${item.id}-${index}`))) })] }) }));
|
|
23
23
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SearchPublicGroupInfo } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
interface ContactSearchCommunitySectionProps {
|
|
3
|
+
groups: SearchPublicGroupInfo[];
|
|
4
|
+
total: number;
|
|
5
|
+
onOpenGroup: (groupID: string) => void;
|
|
6
|
+
onViewAll: () => void;
|
|
7
|
+
}
|
|
8
|
+
declare const ContactSearchCommunitySection: ({ groups, total, onOpenGroup, onViewAll, }: ContactSearchCommunitySectionProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default ContactSearchCommunitySection;
|
|
10
|
+
//# sourceMappingURL=ContactSearchCommunitySection.d.ts.map
|
package/dist/components/conversation-inbox/contact-search/ContactSearchCommunitySection.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContactSearchCommunitySection.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchCommunitySection.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAM1E,UAAU,kCAAkC;IAC1C,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAGD,QAAA,MAAM,6BAA6B,GAAI,4CAKpC,kCAAkC,4CAwBpC,CAAC;AAEF,eAAe,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
|
|
5
|
+
import ContactSearchViewMoreBar from "./ContactSearchViewMoreBar";
|
|
6
|
+
import PublicGroupResultItem from "./PublicGroupResultItem";
|
|
7
|
+
import { CONTACT_SEARCH_PREVIEW_LIMIT } from "./constants";
|
|
8
|
+
// `Tất cả` preview of the `Cộng đồng` tab: same cards, capped like contacts.
|
|
9
|
+
const ContactSearchCommunitySection = ({ groups, total, onOpenGroup, onViewAll, }) => {
|
|
10
|
+
const { t } = useTranslation("conversation-inbox");
|
|
11
|
+
// Guards a short server total so a full page still gets its hand-off.
|
|
12
|
+
const matchCount = Math.max(total, groups.length);
|
|
13
|
+
return (_jsxs("section", { "data-cp-name": "ContactSearchCommunitySection", children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.community"), count: matchCount || undefined, onSelect: onViewAll }), groups.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT).map((group) => (_jsx(PublicGroupResultItem, { group: group, onOpen: onOpenGroup }, group.groupID))), matchCount > CONTACT_SEARCH_PREVIEW_LIMIT && (_jsx(ContactSearchViewMoreBar, { onSelect: onViewAll }))] }));
|
|
14
|
+
};
|
|
15
|
+
export default ContactSearchCommunitySection;
|
|
@@ -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":"AAgCA,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"}
|
|
@@ -10,6 +10,7 @@ import { useChatNavigate } from "../../../hooks/conversation/useChatNavigate";
|
|
|
10
10
|
import { useSearchContacts } from "../../../hooks/search/useSearchContacts";
|
|
11
11
|
import { useSearchPublicGroups } from "../../../hooks/search/useSearchPublicGroups";
|
|
12
12
|
import useConversationStore from "../../../store/conversation";
|
|
13
|
+
import ContactSearchCommunitySection from "./ContactSearchCommunitySection";
|
|
13
14
|
import ContactSearchFilters from "./ContactSearchFilters";
|
|
14
15
|
import ContactSearchResultItem from "./ContactSearchResultItem";
|
|
15
16
|
import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
|
|
@@ -46,9 +47,12 @@ const ContactSearchPanel = ({ open, onClose }) => {
|
|
|
46
47
|
type: activeType,
|
|
47
48
|
enabled: open,
|
|
48
49
|
});
|
|
50
|
+
// `Tất cả` and `Cộng đồng` share one query, so switching tabs hits the cache.
|
|
49
51
|
const publicGroups = useSearchPublicGroups({
|
|
50
52
|
searchTerm: trimmedKeyword,
|
|
51
|
-
enabled: open &&
|
|
53
|
+
enabled: open &&
|
|
54
|
+
(activeTab === ContactSearchTab.All ||
|
|
55
|
+
activeTab === ContactSearchTab.Community),
|
|
52
56
|
});
|
|
53
57
|
const handleOpenGroup = (groupID) => {
|
|
54
58
|
const target = { groupId: groupID };
|
|
@@ -72,46 +76,53 @@ const ContactSearchPanel = ({ open, onClose }) => {
|
|
|
72
76
|
setActiveType(undefined);
|
|
73
77
|
};
|
|
74
78
|
const renderContactItems = (items) => items.map((contact) => (_jsx(ContactSearchResultItem, { contact: contact, searchTerm: trimmedKeyword, onSelect: handleSelectContact }, contact.id)));
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
const renderInitialState = (testId = "contact-search-initial") => (_jsx(ContactSearchStateMessage, { testId: testId, children: t("contact_search.initial") }));
|
|
80
|
+
const renderLoadingState = () => (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
|
|
81
|
+
const renderFailedState = (testId = "contact-search-failed") => (_jsx(ContactSearchStateMessage, { testId: testId, children: t("contact_search.failed") }));
|
|
82
|
+
const renderNoResultState = (testId = "contact-search-no-result") => (_jsxs(ContactSearchStateMessage, { testId: testId, children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
|
|
83
|
+
// The `Liên hệ` tab's not-ready states before it can render results;
|
|
84
|
+
// returns null once there is a list to show.
|
|
77
85
|
const renderPlaceholderState = () => {
|
|
78
|
-
if (!trimmedKeyword)
|
|
79
|
-
return (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
return (
|
|
86
|
-
}
|
|
87
|
-
if (contacts.length === 0) {
|
|
88
|
-
return (_jsxs(ContactSearchStateMessage, { testId: "contact-search-no-result", children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
|
|
89
|
-
}
|
|
86
|
+
if (!trimmedKeyword)
|
|
87
|
+
return renderInitialState();
|
|
88
|
+
if (isLoading)
|
|
89
|
+
return renderLoadingState();
|
|
90
|
+
if (isError)
|
|
91
|
+
return renderFailedState();
|
|
92
|
+
if (contacts.length === 0)
|
|
93
|
+
return renderNoResultState();
|
|
90
94
|
return null;
|
|
91
95
|
};
|
|
92
|
-
// `Tất cả`
|
|
93
|
-
//
|
|
96
|
+
// `Tất cả` previews contacts, then public groups; the message section
|
|
97
|
+
// belongs to the message-search ticket and goes between the two.
|
|
94
98
|
const renderAllTab = () => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
if (!trimmedKeyword)
|
|
100
|
+
return renderInitialState();
|
|
101
|
+
if (isLoading || publicGroups.isLoading)
|
|
102
|
+
return renderLoadingState();
|
|
103
|
+
if (isError && publicGroups.isError)
|
|
104
|
+
return renderFailedState();
|
|
105
|
+
// A source that failed drops its own section instead of the whole tab.
|
|
106
|
+
const showContacts = !isError && contacts.length > 0;
|
|
107
|
+
const showGroups = !publicGroups.isError && publicGroups.groups.length > 0;
|
|
108
|
+
if (!showContacts && !showGroups)
|
|
109
|
+
return renderNoResultState();
|
|
110
|
+
const hasMoreContacts = contacts.length > CONTACT_SEARCH_PREVIEW_LIMIT || !!hasNextPage;
|
|
99
111
|
const openContactsTab = () => handleChangeTab(ContactSearchTab.Contacts);
|
|
100
|
-
return (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.contacts"), count: totalElements || undefined, onSelect: openContactsTab }), renderContactItems(contacts.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT)),
|
|
112
|
+
return (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [showContacts && (_jsxs("section", { children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.contacts"), count: totalElements || undefined, onSelect: openContactsTab }), renderContactItems(contacts.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT)), hasMoreContacts && (_jsx(ContactSearchViewMoreBar, { onSelect: openContactsTab }))] })), showGroups && (_jsx(ContactSearchCommunitySection, { groups: publicGroups.groups, total: publicGroups.total, onOpenGroup: handleOpenGroup, onViewAll: () => handleChangeTab(ContactSearchTab.Community) }))] }));
|
|
101
113
|
};
|
|
102
114
|
const renderContactList = () => (_jsx("div", { id: SCROLL_TARGET_ID, className: "flex-1 overflow-y-auto", children: _jsx(InfiniteScroll, { dataLength: contacts.length, next: fetchNextPage, hasMore: !!hasNextPage, scrollableTarget: SCROLL_TARGET_ID, loader: isFetchingNextPage ? (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) })) : null, children: renderContactItems(contacts) }) }));
|
|
103
115
|
const renderCommunityTab = () => {
|
|
104
116
|
if (!trimmedKeyword) {
|
|
105
|
-
return (
|
|
106
|
-
}
|
|
107
|
-
if (publicGroups.isLoading) {
|
|
108
|
-
return (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
|
|
117
|
+
return renderInitialState("contact-search-community-initial");
|
|
109
118
|
}
|
|
119
|
+
if (publicGroups.isLoading)
|
|
120
|
+
return renderLoadingState();
|
|
110
121
|
if (publicGroups.isError) {
|
|
111
|
-
return (
|
|
122
|
+
return renderFailedState("contact-search-community-failed");
|
|
112
123
|
}
|
|
113
124
|
if (publicGroups.groups.length === 0) {
|
|
114
|
-
return (
|
|
125
|
+
return renderNoResultState("contact-search-community-no-result");
|
|
115
126
|
}
|
|
116
127
|
return (_jsx("div", { id: COMMUNITY_SCROLL_TARGET_ID, className: "flex-1 overflow-y-auto", children: _jsx(InfiniteScroll, { dataLength: publicGroups.groups.length, next: publicGroups.fetchNextPage, hasMore: !!publicGroups.hasNextPage, scrollableTarget: COMMUNITY_SCROLL_TARGET_ID, loader: publicGroups.isFetchingNextPage ? (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) })) : null, children: publicGroups.groups.map((group) => (_jsx(PublicGroupResultItem, { group: group, onOpen: handleOpenGroup }, group.groupID))) }) }));
|
|
117
128
|
};
|
|
@@ -2,8 +2,8 @@ import { ContactSearchType } from "../../../types/dto";
|
|
|
2
2
|
export const CONTACT_SEARCH_DEBOUNCE_MS = 300;
|
|
3
3
|
export const CONTACT_SEARCH_ICON_SIZE = 18;
|
|
4
4
|
export const CONTACT_SEARCH_AVATAR_SIZE = 48;
|
|
5
|
-
// `Tất cả`
|
|
6
|
-
// seller over to the full `Liên hệ` tab.
|
|
5
|
+
// `Tất cả` caps each section's preview (contacts, public groups) before handing
|
|
6
|
+
// the seller over to the full `Liên hệ` / `Cộng đồng` tab.
|
|
7
7
|
export const CONTACT_SEARCH_PREVIEW_LIMIT = 5;
|
|
8
8
|
export var ContactSearchTab;
|
|
9
9
|
(function (ContactSearchTab) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,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,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,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,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAyb3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -74,12 +74,6 @@ const MessageList = (props) => {
|
|
|
74
74
|
(!isConfirmedNonMember &&
|
|
75
75
|
permissions.sendMessage &&
|
|
76
76
|
!isRestrictedByGroupMute);
|
|
77
|
-
// Some hosts run pin without their backend returning PIN_MESSAGE yet, so
|
|
78
|
-
// the permission check would hide a feature that is live for them. Group
|
|
79
|
-
// membership is still required either way.
|
|
80
|
-
const canPinMessage = !isConfirmedGroup ||
|
|
81
|
-
(!isConfirmedNonMember &&
|
|
82
|
-
(permissions.pinMessage || allowPinMessageWithoutPermission));
|
|
83
77
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
84
78
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
85
79
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
@@ -104,6 +98,14 @@ const MessageList = (props) => {
|
|
|
104
98
|
groupId: rawGroupId,
|
|
105
99
|
sessionId: latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.id,
|
|
106
100
|
});
|
|
101
|
+
// The host flag covers backends without PIN_MESSAGE yet; group membership
|
|
102
|
+
// is still required either way.
|
|
103
|
+
const canPinInGroup = !isConfirmedNonMember &&
|
|
104
|
+
(permissions.pinMessage || !!allowPinMessageWithoutPermission);
|
|
105
|
+
// Only members manage a support thread's pins, so an agent joins first.
|
|
106
|
+
const canPinMessage = isConfirmedGroup
|
|
107
|
+
? canPinInGroup
|
|
108
|
+
: !rawGroupId || isJoined === true;
|
|
107
109
|
// A stale isViewOnlyMember=true only ever clears via the WASM join-group
|
|
108
110
|
// path or a WS event — neither fires for a support thread joined via
|
|
109
111
|
// useJoinSession's REST call. Sync it off once isJoined confirms true.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAIjB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAIjB,MAAM,8BAA8B,CAAC;AAetC,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsDD,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAWjC,CAAC;AA+DF,eAAO,MAAM,sBAAsB,GAAI,wDAMpC,4BAA4B;;;CA2J9B,CAAC"}
|
|
@@ -4,8 +4,7 @@ import { GroupStatus, GroupVisibility, SessionType, } from "@droppii-org/wasm-cl
|
|
|
4
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
5
5
|
import { apiInstance } from "../../services/api";
|
|
6
6
|
import { ENDPOINTS } from "../../services/routes";
|
|
7
|
-
import {
|
|
8
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
7
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
9
8
|
import useConversationStore from "../../store/conversation";
|
|
10
9
|
import { getGroupIdFromConversationId, getPeerUserIdFromConversationId, resolveConversationBotId, } from "../../utils/conversation";
|
|
11
10
|
import useAuthStore from "../../store/auth";
|
|
@@ -119,9 +118,7 @@ export const useResolveConversation = ({ conversationId, userId, groupId, botId,
|
|
|
119
118
|
// conversationData looks truthy; only a rename to something else (a newer
|
|
120
119
|
// resolve superseding this one) or an explicit reject below clears it.
|
|
121
120
|
const pendingTargetRef = useRef(null);
|
|
122
|
-
const
|
|
123
|
-
const isSdkReady = connectStatus === ConnectStatus.Connected &&
|
|
124
|
-
syncStatus !== SyncStatus.Loading;
|
|
121
|
+
const isSdkReady = useCanLoadChatData();
|
|
125
122
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
126
123
|
const removedFromGroupModalOpen = useConversationStore((state) => state.removedFromGroupModalOpen);
|
|
127
124
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,YAqnB1B,CAAC"}
|
|
@@ -234,7 +234,9 @@ export const useGlobalEvent = () => {
|
|
|
234
234
|
}
|
|
235
235
|
updateConnectStatus(ConnectStatus.Disconnected);
|
|
236
236
|
};
|
|
237
|
+
// data is `reinstalled`: true when this device starts from an empty local DB.
|
|
237
238
|
const syncStartHandler = ({ data }) => {
|
|
239
|
+
useBootStore.getState().markSyncStarted(data === true || data === "true");
|
|
238
240
|
updateSyncStatus(SyncStatus.Loading);
|
|
239
241
|
};
|
|
240
242
|
const syncProgressHandler = ({ data }) => {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether threads can be opened and their history read: connected and not mid-sync. A fresh
|
|
3
|
+
* device's first sync counts as done once its conversations are stored, since history then
|
|
4
|
+
* loads on demand while the latest message of each conversation is still being pulled.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useCanLoadChatData: () => boolean;
|
|
7
|
+
//# sourceMappingURL=useCanLoadChatData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanLoadChatData.d.ts","sourceRoot":"","sources":["../../../src/hooks/init/useCanLoadChatData.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,eAO9B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
3
|
+
import useBootStore, { selectIsSyncingInBackground } from "../../store/boot";
|
|
4
|
+
import { ConnectStatus, SyncStatus } from "../../types/chat";
|
|
5
|
+
/**
|
|
6
|
+
* Whether threads can be opened and their history read: connected and not mid-sync. A fresh
|
|
7
|
+
* device's first sync counts as done once its conversations are stored, since history then
|
|
8
|
+
* loads on demand while the latest message of each conversation is still being pulled.
|
|
9
|
+
*/
|
|
10
|
+
export const useCanLoadChatData = () => {
|
|
11
|
+
const { connectStatus, syncStatus } = useChatContext();
|
|
12
|
+
const isSyncingInBackground = useBootStore(selectIsSyncingInBackground);
|
|
13
|
+
return (connectStatus === ConnectStatus.Connected &&
|
|
14
|
+
(syncStatus !== SyncStatus.Loading || isSyncingInBackground));
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanPinMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useCanPinMessage.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gBAAgB,eA2B5B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
2
|
+
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
3
|
+
import { isSupportThreadJoinedHere } from "../../hooks/session/useAgentSupportThreadJoin";
|
|
4
|
+
import { useIsJoinedGroup } from "../../hooks/session/useIsJoinedGroup";
|
|
5
|
+
import useAuthStore from "../../store/auth";
|
|
6
|
+
import useConversationStore from "../../store/conversation";
|
|
7
|
+
import { isGroupChat, isSupportSessionAgentView } from "../../utils/conversation";
|
|
8
|
+
// Pin rule of DChatConversationDetail, shared by its toolbox and pinned panel.
|
|
9
|
+
// Desk's MessageList keeps its own rule, which also checks group membership.
|
|
10
|
+
export const useCanPinMessage = () => {
|
|
11
|
+
const { user, allowPinMessageWithoutPermission } = useChatContext();
|
|
12
|
+
const authUserID = useAuthStore((state) => state.userID);
|
|
13
|
+
const conversation = useConversationStore((state) => state.conversationData);
|
|
14
|
+
const permissions = useGroupPermission(conversation === null || conversation === void 0 ? void 0 : conversation.groupID);
|
|
15
|
+
const isGroup = conversation ? isGroupChat(conversation) : false;
|
|
16
|
+
// An agent on someone else's support thread manages pins only once joined.
|
|
17
|
+
const isAgentSupportThread = !!conversation &&
|
|
18
|
+
!isGroup &&
|
|
19
|
+
isSupportSessionAgentView(conversation, (user === null || user === void 0 ? void 0 : user.userID) || authUserID);
|
|
20
|
+
const { isJoined } = useIsJoinedGroup(isAgentSupportThread ? conversation === null || conversation === void 0 ? void 0 : conversation.groupID : undefined);
|
|
21
|
+
if (isAgentSupportThread) {
|
|
22
|
+
return (!conversation.groupID ||
|
|
23
|
+
isJoined === true ||
|
|
24
|
+
isSupportThreadJoinedHere(conversation.conversationID));
|
|
25
|
+
}
|
|
26
|
+
// Some hosts run pin without their backend returning PIN_MESSAGE yet, so
|
|
27
|
+
// the permission check would hide a feature that is live for them.
|
|
28
|
+
return (!isGroup || permissions.pinMessage || !!allowPinMessageWithoutPermission);
|
|
29
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAkBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA4BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;CAoZnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAmBF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
|
|
@@ -7,9 +7,8 @@ import { useTranslation } from "react-i18next";
|
|
|
7
7
|
import { useLatest, useRequest } from "ahooks";
|
|
8
8
|
import emitter, { emit } from "../../utils/events";
|
|
9
9
|
import isEmpty from "lodash/isEmpty";
|
|
10
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
11
10
|
import useAuthStore from "../../store/auth";
|
|
12
|
-
import {
|
|
11
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
13
12
|
import { GROUP_NOTIFICATION_MESSAGE_TYPES, isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType } from "../../utils/messageLog";
|
|
14
13
|
import { isEventCustomMessage, isRenderableCustomMessage, } from "../../utils/customMessage";
|
|
15
14
|
import { isGroupConversation } from "../../utils/imCommon";
|
|
@@ -60,12 +59,11 @@ export const useMessage = (conversationId, searchClientMsgID, enabled = true, us
|
|
|
60
59
|
// Host-supplied, threaded down from the screen props the same way
|
|
61
60
|
// onCopyMessageLink is. Omit it and the SDK raises its own antd toast.
|
|
62
61
|
onSearchMessageRevoked) => {
|
|
63
|
-
const
|
|
62
|
+
const canLoadChatData = useCanLoadChatData();
|
|
64
63
|
const { t } = useTranslation();
|
|
65
64
|
const userID = useAuthStore((state) => state.userID);
|
|
66
65
|
const canLoad = enabled &&
|
|
67
|
-
|
|
68
|
-
syncStatus !== SyncStatus.Loading &&
|
|
66
|
+
canLoadChatData &&
|
|
69
67
|
!!conversationId &&
|
|
70
68
|
!!userID;
|
|
71
69
|
const [loadState, setLoadState] = useState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUnreadMessageIndicator.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useUnreadMessageIndicator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"useUnreadMessageIndicator.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useUnreadMessageIndicator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAShE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAoEnD,UAAU,+BAA+B;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,qDAIvC,+BAA+B;;;;;;CA6FjC,CAAC"}
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { useLatest } from "ahooks";
|
|
4
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
5
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
6
5
|
import useAuthStore from "../../store/auth";
|
|
7
6
|
import useConversationStore from "../../store/conversation";
|
|
8
|
-
import {
|
|
7
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
9
8
|
// Mark-as-read waits for this lookup, so a call that never settles must not
|
|
10
9
|
// leave the thread unread.
|
|
11
10
|
export const FIRST_UNREAD_LOOKUP_TIMEOUT_MS = 5000;
|
|
@@ -61,13 +60,12 @@ const isOlderThan = (message, boundary) => message.seq && boundary.seq
|
|
|
61
60
|
*/
|
|
62
61
|
export const useUnreadMessageIndicator = ({ conversationId = "", searchClientMsgID, messageList, }) => {
|
|
63
62
|
var _a;
|
|
64
|
-
const
|
|
63
|
+
const canLoadChatData = useCanLoadChatData();
|
|
65
64
|
const userID = useAuthStore((state) => state.userID);
|
|
66
65
|
const storedConversationId = useConversationStore((state) => { var _a; return (_a = state.conversationData) === null || _a === void 0 ? void 0 : _a.conversationID; });
|
|
67
66
|
// Waiting for the store to hold this conversation also waits for its open
|
|
68
67
|
// target, which some hosts set a moment after the selected id.
|
|
69
|
-
const canLookup =
|
|
70
|
-
syncStatus !== SyncStatus.Loading &&
|
|
68
|
+
const canLookup = canLoadChatData &&
|
|
71
69
|
!!conversationId &&
|
|
72
70
|
!!userID &&
|
|
73
71
|
storedConversationId === conversationId;
|
|
@@ -6,6 +6,7 @@ interface UseSearchPublicGroupsParams {
|
|
|
6
6
|
}
|
|
7
7
|
export declare const useSearchPublicGroups: ({ searchTerm, enabled, pageSize, }: UseSearchPublicGroupsParams) => {
|
|
8
8
|
groups: SearchPublicGroupInfo[];
|
|
9
|
+
total: number;
|
|
9
10
|
fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<import("@droppii-org/wasm-client-sdk").SearchPublicGroupsResult, unknown>, Error>>;
|
|
10
11
|
hasNextPage: boolean;
|
|
11
12
|
isFetchingNextPage: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSearchPublicGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchPublicGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAKtC,UAAU,2BAA2B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,eAAO,MAAM,qBAAqB,GAAI,oCAInC,2BAA2B
|
|
1
|
+
{"version":3,"file":"useSearchPublicGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchPublicGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAKtC,UAAU,2BAA2B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,eAAO,MAAM,qBAAqB,GAAI,oCAInC,2BAA2B;;;;;;;;CA0D7B,CAAC"}
|
|
@@ -9,6 +9,7 @@ import { PAGE_SIZE } from "../../constants";
|
|
|
9
9
|
// same call chat-mobile uses for its `Cộng đồng` tab, so both platforms return
|
|
10
10
|
// the identical public-group set (joined and not yet joined).
|
|
11
11
|
export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = PAGE_SIZE.medium, }) => {
|
|
12
|
+
var _a, _b, _c;
|
|
12
13
|
const trimmedSearchTerm = searchTerm.trim();
|
|
13
14
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError } = useInfiniteQuery({
|
|
14
15
|
initialPageParam: 0,
|
|
@@ -29,6 +30,9 @@ export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = P
|
|
|
29
30
|
return fetched < ((_a = lastPage === null || lastPage === void 0 ? void 0 : lastPage.total) !== null && _a !== void 0 ? _a : 0) ? fetched : undefined;
|
|
30
31
|
},
|
|
31
32
|
enabled: enabled && trimmedSearchTerm !== "",
|
|
33
|
+
// `Tất cả` waits for this query, so a failing engine must not hold it
|
|
34
|
+
// through the default three retries.
|
|
35
|
+
retry: 1,
|
|
32
36
|
});
|
|
33
37
|
// A group can repeat across offsets while the backing list shifts.
|
|
34
38
|
const groups = useMemo(() => {
|
|
@@ -46,6 +50,8 @@ export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = P
|
|
|
46
50
|
}, [data]);
|
|
47
51
|
return {
|
|
48
52
|
groups,
|
|
53
|
+
// Every page carries the same server-side total for the keyword.
|
|
54
|
+
total: (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.pages) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.total) !== null && _c !== void 0 ? _c : 0,
|
|
49
55
|
fetchNextPage,
|
|
50
56
|
hasNextPage,
|
|
51
57
|
isFetchingNextPage,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ConversationItem } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
export declare const isSupportThreadJoinedHere: (conversationId?: string) => boolean;
|
|
2
3
|
/**
|
|
3
4
|
* CRM's join gate for the thread detail: an agent viewing a support thread
|
|
4
5
|
* they aren't a member of can't send until they join. Opt-in — only a host
|