@droppii-org/chat-sdk 0.1.96 → 0.1.97
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.d.ts +7 -0
- package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -0
- package/dist/components/compose/ComposeNewMessageModal.js +61 -0
- package/dist/components/conversation/ConversationByGroupItem.d.ts +7 -0
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -0
- package/dist/components/conversation/ConversationByGroupItem.js +50 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +15 -6
- package/dist/components/group/CreateGroupForm.d.ts +7 -0
- package/dist/components/group/CreateGroupForm.d.ts.map +1 -0
- package/dist/components/group/CreateGroupForm.js +74 -0
- package/dist/components/group/GroupMemberPicker.d.ts +8 -0
- package/dist/components/group/GroupMemberPicker.d.ts.map +1 -0
- package/dist/components/group/GroupMemberPicker.js +36 -0
- package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
- package/dist/components/session/DeskAssignedSession.js +4 -1
- package/dist/components/session/SessionFilterMenu.d.ts +3 -1
- package/dist/components/session/SessionFilterMenu.d.ts.map +1 -1
- package/dist/components/session/SessionFilterMenu.js +8 -5
- package/dist/components/session/sessionMenuItems.d.ts +3 -1
- package/dist/components/session/sessionMenuItems.d.ts.map +1 -1
- package/dist/components/session/sessionMenuItems.js +11 -1
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +12 -0
- package/dist/hooks/group/useCreateGroup.d.ts +12 -0
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -0
- package/dist/hooks/group/useCreateGroup.js +62 -0
- package/dist/hooks/group/useEligibleAccounts.d.ts +10 -0
- package/dist/hooks/group/useEligibleAccounts.d.ts.map +1 -0
- package/dist/hooks/group/useEligibleAccounts.js +23 -0
- package/dist/hooks/group/useGetGroupCandidates.d.ts +9 -0
- package/dist/hooks/group/useGetGroupCandidates.d.ts.map +1 -0
- package/dist/hooks/group/useGetGroupCandidates.js +20 -0
- package/dist/hooks/group/useJoinedGroups.d.ts +13 -0
- package/dist/hooks/group/useJoinedGroups.d.ts.map +1 -0
- package/dist/hooks/group/useJoinedGroups.js +33 -0
- package/dist/locales/vi/common.json +1 -0
- package/dist/locales/vi/conversation-inbox.json +27 -0
- package/dist/screens/desk-message/index.d.ts.map +1 -1
- package/dist/screens/desk-message/index.js +4 -1
- package/dist/services/query.d.ts +2 -0
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +2 -0
- package/dist/services/routes.d.ts +1 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +1 -0
- package/dist/store/session.d.ts +3 -0
- package/dist/store/session.d.ts.map +1 -1
- package/dist/store/session.js +3 -1
- package/dist/styles/global.css +1 -1
- package/dist/types/dto.d.ts +12 -0
- package/dist/types/dto.d.ts.map +1 -1
- package/dist/utils/groupName.d.ts +8 -0
- package/dist/utils/groupName.d.ts.map +1 -0
- package/dist/utils/groupName.js +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface ComposeNewMessageModalProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
}
|
|
5
|
+
declare const ComposeNewMessageModal: ({ open, onClose, }: ComposeNewMessageModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ComposeNewMessageModal;
|
|
7
|
+
//# sourceMappingURL=ComposeNewMessageModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComposeNewMessageModal.d.ts","sourceRoot":"","sources":["../../../src/components/compose/ComposeNewMessageModal.tsx"],"names":[],"mappings":"AAeA,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID,QAAA,MAAM,sBAAsB,GAAI,oBAG7B,2BAA2B,4CAyI7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useDebounce } from "ahooks";
|
|
4
|
+
import { Avatar, Empty, Input, Modal, Spin } from "antd";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
7
|
+
import { useTranslation } from "react-i18next";
|
|
8
|
+
import { SessionType } from "@droppii-org/wasm-client-sdk";
|
|
9
|
+
import { Icon } from "../../components/icon";
|
|
10
|
+
import { useEligibleAccounts } from "../../hooks/group/useEligibleAccounts";
|
|
11
|
+
import { DChatSDK } from "../../constants/sdk";
|
|
12
|
+
import useConversationStore from "../../store/conversation";
|
|
13
|
+
import CreateGroupForm from "../../components/group/CreateGroupForm";
|
|
14
|
+
const ComposeNewMessageModal = ({ open, onClose, }) => {
|
|
15
|
+
const { t } = useTranslation("conversation-inbox");
|
|
16
|
+
const [step, setStep] = useState("compose");
|
|
17
|
+
const [search, setSearch] = useState("");
|
|
18
|
+
const [openingUserID, setOpeningUserID] = useState(null);
|
|
19
|
+
const debouncedSearch = useDebounce(search, { wait: 300 });
|
|
20
|
+
const { accounts, isLoading } = useEligibleAccounts(debouncedSearch, open);
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
const pathname = usePathname();
|
|
23
|
+
const searchParams = useSearchParams();
|
|
24
|
+
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
25
|
+
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
26
|
+
const handleClose = () => {
|
|
27
|
+
setSearch("");
|
|
28
|
+
setStep("compose");
|
|
29
|
+
onClose();
|
|
30
|
+
};
|
|
31
|
+
const handleAfterClose = () => {
|
|
32
|
+
// Reset only after the close animation finishes so the outgoing
|
|
33
|
+
// content doesn't visibly swap mid-transition.
|
|
34
|
+
setStep("compose");
|
|
35
|
+
};
|
|
36
|
+
const handleOpenDirectMessage = async (account) => {
|
|
37
|
+
if (openingUserID)
|
|
38
|
+
return;
|
|
39
|
+
setOpeningUserID(account.userID);
|
|
40
|
+
try {
|
|
41
|
+
const { data: conversation } = await DChatSDK.getOneConversation({
|
|
42
|
+
sourceID: account.userID,
|
|
43
|
+
sessionType: SessionType.Single,
|
|
44
|
+
});
|
|
45
|
+
setConversationData(conversation);
|
|
46
|
+
setSelectedConversationId(conversation.conversationID);
|
|
47
|
+
const newSearchParams = new URLSearchParams(searchParams);
|
|
48
|
+
newSearchParams.set("threadId", conversation.conversationID);
|
|
49
|
+
newSearchParams.delete("userId");
|
|
50
|
+
newSearchParams.delete("groupId");
|
|
51
|
+
newSearchParams.delete("botId");
|
|
52
|
+
router.push(`${pathname}?${newSearchParams.toString()}`);
|
|
53
|
+
handleClose();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
setOpeningUserID(null);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return (_jsx(Modal, { title: step === "create-group" ? t("create_group.title") : t("compose.title"), open: open, onCancel: handleClose, afterClose: handleAfterClose, footer: null, centered: true, forceRender: true, width: step === "create-group" ? 720 : undefined, getContainer: false, children: step === "compose" ? (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Input, { size: "large", value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("compose.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }) }), _jsxs("button", { type: "button", className: "flex items-center gap-3 rounded-xl bg-primary-50 px-4 py-3.5 text-left transition-colors hover:bg-primary-100", onClick: () => setStep("create-group"), children: [_jsx("span", { className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-white", children: _jsx(Icon, { icon: "user-three-b", size: 22, className: "text-primary-400" }) }), _jsx("span", { className: "flex-1 text-base font-semibold text-gray-900", children: t("compose.create_group") }), _jsx(Icon, { icon: "angle-right-o", size: 18, className: "text-gray-400" })] }), !debouncedSearch && (_jsxs("div", { className: "flex items-center gap-2 px-1", children: [_jsx("span", { className: "shrink-0 text-xs font-semibold uppercase tracking-wide text-gray-400", children: t("compose.recent_suggestions") }), _jsx("span", { className: "h-px flex-1 bg-gray-100" })] })), _jsx("div", { className: "max-h-80 overflow-y-auto", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("compose.no_member_found") })) : (accounts.map((account) => (_jsxs("div", { className: "flex cursor-pointer items-center gap-3 border-b border-gray-50 px-1 py-3 transition-colors last:border-b-0 hover:bg-gray-50", onClick: () => handleOpenDirectMessage(account), children: [_jsx(Avatar, { src: account.avatar, size: 44, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), openingUserID === account.userID && _jsx(Spin, { size: "small" })] }, account.userID)))) })] })) : (_jsx(CreateGroupForm, { onBack: () => setStep("compose"), onDone: handleClose })) }));
|
|
60
|
+
};
|
|
61
|
+
export default ComposeNewMessageModal;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { GroupItem } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
interface ConversationByGroupItemProps {
|
|
3
|
+
group: GroupItem;
|
|
4
|
+
}
|
|
5
|
+
declare const ConversationByGroupItem: ({ group }: ConversationByGroupItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ConversationByGroupItem;
|
|
7
|
+
//# sourceMappingURL=ConversationByGroupItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAe,MAAM,8BAA8B,CAAC;AAMtE,UAAU,4BAA4B;IACpC,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,QAAA,MAAM,uBAAuB,GAAI,WAAW,4BAA4B,4CAiEvE,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Avatar } from "antd";
|
|
5
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { SessionType } from "@droppii-org/wasm-client-sdk";
|
|
8
|
+
import useConversationStore from "../../store/conversation";
|
|
9
|
+
import { DChatSDK } from "../../constants/sdk";
|
|
10
|
+
import { formatTimestamp } from "../../utils/common";
|
|
11
|
+
import ThreadCard from "./ThreadCard";
|
|
12
|
+
const ConversationByGroupItem = ({ group }) => {
|
|
13
|
+
var _a;
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const [isOpening, setIsOpening] = useState(false);
|
|
16
|
+
const router = useRouter();
|
|
17
|
+
const pathname = usePathname();
|
|
18
|
+
const searchParams = useSearchParams();
|
|
19
|
+
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
20
|
+
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
21
|
+
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
22
|
+
const isSelected = useConversationStore((state) => state.selectedSourceId === group.groupID);
|
|
23
|
+
const handleClick = async () => {
|
|
24
|
+
if (isOpening)
|
|
25
|
+
return;
|
|
26
|
+
setIsOpening(true);
|
|
27
|
+
try {
|
|
28
|
+
const { data: conversation } = await DChatSDK.getOneConversation({
|
|
29
|
+
sourceID: group.groupID,
|
|
30
|
+
sessionType: SessionType.Group,
|
|
31
|
+
});
|
|
32
|
+
setConversationData(conversation);
|
|
33
|
+
updateConversationList([conversation], "filter");
|
|
34
|
+
setSelectedConversationId(conversation.conversationID);
|
|
35
|
+
const newSearchParams = new URLSearchParams(searchParams);
|
|
36
|
+
newSearchParams.set("threadId", conversation.conversationID);
|
|
37
|
+
newSearchParams.delete("userId");
|
|
38
|
+
newSearchParams.delete("groupId");
|
|
39
|
+
newSearchParams.delete("botId");
|
|
40
|
+
router.push(`${pathname}?${newSearchParams.toString()}`);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
setIsOpening(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return (_jsx(ThreadCard, { onClick: handleClick, isSelected: isSelected, preview: t("member_count", { count: group.memberCount }), time: group.createTime
|
|
47
|
+
? formatTimestamp(group.createTime, { hasTime: true })
|
|
48
|
+
: undefined, avatar: _jsx(Avatar, { size: 48, src: group.faceURL, children: ((_a = group.groupName) === null || _a === void 0 ? void 0 : _a.charAt(0)) || "G" }), title: _jsx("h3", { className: "truncate text-sm font-semibold text-gray-900", children: group.groupName }) }));
|
|
49
|
+
};
|
|
50
|
+
export default ConversationByGroupItem;
|
|
@@ -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":"AA4BA,QAAA,MAAM,oBAAoB,+CAwRzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -11,18 +11,22 @@ import SearchConversation from "../../components/search-conversation";
|
|
|
11
11
|
import useSessionStore from "../../store/session";
|
|
12
12
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
13
13
|
import ConversationBySessionItem from "./ConversationBySessionItem";
|
|
14
|
+
import ConversationByGroupItem from "./ConversationByGroupItem";
|
|
14
15
|
import { useGetSession } from "../../hooks/session/useGetSession";
|
|
15
16
|
import { useSortedSessions } from "../../hooks/session/useSortedSessions";
|
|
16
17
|
import { useSessionLatestPreview } from "../../hooks/session/useSessionLatestPreview";
|
|
17
18
|
import { useGetTeamSessionSummary } from "../../hooks/session/useGetTeamSessionSummary";
|
|
19
|
+
import { useJoinedGroups } from "../../hooks/group/useJoinedGroups";
|
|
18
20
|
import { DChatSDK } from "../../constants/sdk";
|
|
19
21
|
import emitter from "../../utils/events";
|
|
20
|
-
import { getFilterConfig } from "../../components/session/sessionMenuItems";
|
|
22
|
+
import { ALL_GROUPS_KEY, getFilterConfig, } from "../../components/session/sessionMenuItems";
|
|
23
|
+
import ComposeNewMessageModal from "../../components/compose/ComposeNewMessageModal";
|
|
21
24
|
const DeskConversationList = () => {
|
|
22
25
|
var _a, _b;
|
|
23
26
|
const searchInputRef = useRef(null);
|
|
24
27
|
const { t } = useTranslation();
|
|
25
28
|
const [search, setSearch] = useState("");
|
|
29
|
+
const [isComposeOpen, setIsComposeOpen] = useState(false);
|
|
26
30
|
const [showSearch, { setTrue: setShowSearchTrue, setFalse: setShowSearchFalse },] = useBoolean(false);
|
|
27
31
|
const searchParams = useSearchParams();
|
|
28
32
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
@@ -30,11 +34,16 @@ const DeskConversationList = () => {
|
|
|
30
34
|
const conversationList = useConversationStore((state) => state.conversationList);
|
|
31
35
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
32
36
|
const filterSummary = useSessionStore((state) => state.filterSummary);
|
|
37
|
+
const viewMode = useSessionStore((state) => state.viewMode);
|
|
38
|
+
const isAllGroupsView = viewMode === "allGroups";
|
|
33
39
|
const { data: teamSummaries } = useGetTeamSessionSummary();
|
|
34
|
-
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, } = useGetSession({ filter: filterSummary });
|
|
40
|
+
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, } = useGetSession({ filter: filterSummary, options: { isEnabled: !isAllGroupsView } });
|
|
41
|
+
const { groups: joinedGroups, fetchNextPage: fetchNextGroupsPage, hasNextPage: hasNextGroupsPage, isLoading: isJoinedGroupsLoading, } = useJoinedGroups({ enabled: isAllGroupsView });
|
|
35
42
|
const latestPreviewMap = useSessionLatestPreview(rawSessions);
|
|
36
43
|
const sessions = useSortedSessions(rawSessions, latestPreviewMap);
|
|
37
|
-
const currentFilterKey =
|
|
44
|
+
const currentFilterKey = isAllGroupsView
|
|
45
|
+
? ALL_GROUPS_KEY
|
|
46
|
+
: filterSummary.status || filterSummary.tag || "";
|
|
38
47
|
const filterConfig = getFilterConfig(currentFilterKey);
|
|
39
48
|
const pathName = filterSummary.teamId
|
|
40
49
|
? ((_a = teamSummaries === null || teamSummaries === void 0 ? void 0 : teamSummaries.find((team) => team.teamId === filterSummary.teamId)) === null || _a === void 0 ? void 0 : _a.teamName) || ""
|
|
@@ -91,14 +100,14 @@ const DeskConversationList = () => {
|
|
|
91
100
|
setShowSearchTrue();
|
|
92
101
|
}
|
|
93
102
|
setSearch(e.target.value);
|
|
94
|
-
}, className: "rounded-lg text-sm flex-1 h-[36px]", size: "large", allowClear: true, onClick: setShowSearchTrue, value: search, autoFocus: false, onClear: onCloseSearch }), showSearch
|
|
103
|
+
}, 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", { id: "scrollableConversationsDiv", style: {
|
|
95
104
|
height: "100%",
|
|
96
105
|
overflow: showSearch ? "hidden" : "auto",
|
|
97
106
|
position: "relative",
|
|
98
|
-
}, children: [_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((_b = sessions === null || sessions === void 0 ? void 0 : sessions.map) === null || _b === void 0 ? void 0 : _b.call(sessions, (session) => (_jsx(ConversationBySessionItem, { sessionItem: session, latestPreview: latestPreviewMap.get(session.conversationId) }, session.conversationId)))) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
|
|
107
|
+
}, children: [isAllGroupsView ? (isJoinedGroupsLoading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : !(joinedGroups === null || joinedGroups === void 0 ? void 0 : joinedGroups.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: joinedGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: joinedGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group }, 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: (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: {
|
|
99
108
|
body: {
|
|
100
109
|
padding: 0,
|
|
101
110
|
},
|
|
102
|
-
}, getContainer: false, width: "100%", children: _jsx(SearchConversation, { searchTerm: debouncedSearch }) })] })] }));
|
|
111
|
+
}, getContainer: false, width: "100%", children: _jsx(SearchConversation, { searchTerm: debouncedSearch }) })] }), _jsx(ComposeNewMessageModal, { open: isComposeOpen, onClose: () => setIsComposeOpen(false) })] }));
|
|
103
112
|
};
|
|
104
113
|
export default DeskConversationList;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface CreateGroupFormProps {
|
|
2
|
+
onBack: () => void;
|
|
3
|
+
onDone: () => void;
|
|
4
|
+
}
|
|
5
|
+
declare const CreateGroupForm: ({ onBack, onDone }: CreateGroupFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default CreateGroupForm;
|
|
7
|
+
//# sourceMappingURL=CreateGroupForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateGroupForm.d.ts","sourceRoot":"","sources":["../../../src/components/group/CreateGroupForm.tsx"],"names":[],"mappings":"AAcA,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,oBAAoB,oBAAoB,4CA8KhE,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, Input, Upload } from "antd";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { Icon } from "../../components/icon";
|
|
8
|
+
import { useCreateGroup } from "../../hooks/group/useCreateGroup";
|
|
9
|
+
import GroupMemberPicker from "./GroupMemberPicker";
|
|
10
|
+
const GROUP_NAME_MAX_LENGTH = 255;
|
|
11
|
+
const CreateGroupForm = ({ onBack, onDone }) => {
|
|
12
|
+
const { t } = useTranslation("conversation-inbox");
|
|
13
|
+
const { createGroup } = useCreateGroup();
|
|
14
|
+
const [groupName, setGroupName] = useState("");
|
|
15
|
+
// AC-Privacy1: only set at creation time, not editable after — no need
|
|
16
|
+
// for an "update privacy" path anywhere else in this modal.
|
|
17
|
+
const [isPublic, setIsPublic] = useState(false);
|
|
18
|
+
const [avatarFile, setAvatarFile] = useState(null);
|
|
19
|
+
const [avatarPreviewUrl, setAvatarPreviewUrl] = useState("");
|
|
20
|
+
const [selectedMembers, setSelectedMembers] = useState([]);
|
|
21
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
22
|
+
const canSubmit = selectedMembers.length > 0 && !isSubmitting;
|
|
23
|
+
const handleAvatarSelect = (file) => {
|
|
24
|
+
// Local preview only — there's no dedicated avatar-upload endpoint yet.
|
|
25
|
+
// faceURL falls back to this object URL until BE provides one; group
|
|
26
|
+
// creation itself doesn't block on it (Q3/Q4-style non-block gap).
|
|
27
|
+
const previewUrl = URL.createObjectURL(file);
|
|
28
|
+
setAvatarPreviewUrl(previewUrl);
|
|
29
|
+
setAvatarFile({ uid: file.name, name: file.name });
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
const handleSubmit = async () => {
|
|
33
|
+
if (!canSubmit)
|
|
34
|
+
return;
|
|
35
|
+
setIsSubmitting(true);
|
|
36
|
+
try {
|
|
37
|
+
await createGroup({
|
|
38
|
+
groupName,
|
|
39
|
+
isPublic,
|
|
40
|
+
members: selectedMembers,
|
|
41
|
+
faceURL: avatarPreviewUrl,
|
|
42
|
+
});
|
|
43
|
+
onDone();
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
// Error already surfaced via message.error inside useCreateGroup.
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setIsSubmitting(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const privacyOptions = useMemo(() => [
|
|
53
|
+
{
|
|
54
|
+
value: false,
|
|
55
|
+
icon: "lock-b",
|
|
56
|
+
label: t("create_group.privacy_private"),
|
|
57
|
+
description: t("create_group.privacy_private_desc"),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
value: true,
|
|
61
|
+
icon: "globe-b",
|
|
62
|
+
label: t("create_group.privacy_public"),
|
|
63
|
+
description: t("create_group.privacy_public_desc"),
|
|
64
|
+
},
|
|
65
|
+
], [t]);
|
|
66
|
+
return (_jsxs("div", { className: "flex max-h-[70vh] flex-col", children: [_jsxs("button", { type: "button", onClick: onBack, className: "mb-3 flex w-fit items-center gap-1 text-sm text-gray-500 hover:text-gray-700", children: [_jsx(Icon, { icon: "angle-left-o", size: 16 }), t("create_group.back")] }), _jsxs("div", { className: "flex flex-col overflow-y-auto pr-1", children: [_jsxs("div", { className: "flex items-center gap-4 pb-6", children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg", showUploadList: false, beforeUpload: handleAvatarSelect, children: _jsxs("div", { className: "relative flex h-20 w-20 shrink-0 cursor-pointer items-center justify-center rounded-2xl bg-primary-50", children: [avatarPreviewUrl ? (
|
|
67
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
68
|
+
_jsx("img", { src: avatarPreviewUrl, alt: "", className: "h-full w-full rounded-2xl object-cover" })) : (_jsx(Icon, { icon: "user-three-b", size: 32, className: "text-primary-400" })), _jsx("span", { className: "absolute -bottom-1 -right-1 flex h-7 w-7 items-center justify-center rounded-full bg-gray-500", children: _jsx(Icon, { icon: "camera-b", size: 14, className: "text-white" }) })] }) }), _jsx(Input, { size: "large", value: groupName, onChange: (event) => setGroupName(event.target.value.slice(0, GROUP_NAME_MAX_LENGTH)), maxLength: GROUP_NAME_MAX_LENGTH, placeholder: t("create_group.name_placeholder"), className: "flex-1 text-base" })] }), _jsxs("div", { className: "flex flex-col gap-3 border-t border-gray-100 py-6", children: [_jsx("span", { className: "text-base font-semibold text-gray-900", children: t("create_group.privacy_label") }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: privacyOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => setIsPublic(option.value), className: clsx("rounded-xl border-2 p-4 text-left transition-colors", isPublic === option.value
|
|
69
|
+
? "border-primary-400 bg-primary-50"
|
|
70
|
+
: "border-gray-200 bg-white"), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { icon: option.icon, size: 18, className: isPublic === option.value
|
|
71
|
+
? "text-primary-400"
|
|
72
|
+
: "text-gray-400" }), _jsx("span", { className: "text-base font-semibold text-gray-900", children: option.label })] }), _jsx("p", { className: "mt-1.5 text-sm text-gray-500", children: option.description })] }, String(option.value)))) })] }), _jsx("div", { className: "border-t border-gray-100 pt-6", children: _jsx(GroupMemberPicker, { selectedMembers: selectedMembers, onChangeSelectedMembers: setSelectedMembers }) })] }), _jsxs("div", { className: "flex shrink-0 justify-end gap-2 pt-4", children: [_jsx(Button, { onClick: onDone, children: t("create_group.cancel") }), _jsx(Button, { type: "primary", disabled: !canSubmit, loading: isSubmitting, onClick: handleSubmit, children: t("create_group.submit") })] })] }));
|
|
73
|
+
};
|
|
74
|
+
export default CreateGroupForm;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EligibleAccountItem } from "../../hooks/group/useEligibleAccounts";
|
|
2
|
+
interface GroupMemberPickerProps {
|
|
3
|
+
selectedMembers: EligibleAccountItem[];
|
|
4
|
+
onChangeSelectedMembers: (members: EligibleAccountItem[]) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const GroupMemberPicker: ({ selectedMembers, onChangeSelectedMembers, }: GroupMemberPickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default GroupMemberPicker;
|
|
8
|
+
//# sourceMappingURL=GroupMemberPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupMemberPicker.d.ts","sourceRoot":"","sources":["../../../src/components/group/GroupMemberPicker.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,UAAU,sBAAsB;IAC9B,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,uBAAuB,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,iBAAiB,GAAI,+CAGxB,sBAAsB,4CAoIxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useDebounce } from "ahooks";
|
|
4
|
+
import { Avatar, Empty, Input, Spin } from "antd";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { useTranslation } from "react-i18next";
|
|
8
|
+
import { Icon } from "../../components/icon";
|
|
9
|
+
import { useEligibleAccounts } from "../../hooks/group/useEligibleAccounts";
|
|
10
|
+
const GroupMemberPicker = ({ selectedMembers, onChangeSelectedMembers, }) => {
|
|
11
|
+
const { t } = useTranslation("conversation-inbox");
|
|
12
|
+
const [search, setSearch] = useState("");
|
|
13
|
+
const debouncedSearch = useDebounce(search, { wait: 300 });
|
|
14
|
+
const { accounts, isLoading } = useEligibleAccounts(debouncedSearch);
|
|
15
|
+
const selectedIds = new Set(selectedMembers.map((member) => member.userID));
|
|
16
|
+
const toggleMember = (account) => {
|
|
17
|
+
if (selectedIds.has(account.userID)) {
|
|
18
|
+
onChangeSelectedMembers(selectedMembers.filter((member) => member.userID !== account.userID));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
onChangeSelectedMembers([...selectedMembers, account]);
|
|
22
|
+
};
|
|
23
|
+
const handleRemoveMember = (userID) => {
|
|
24
|
+
onChangeSelectedMembers(selectedMembers.filter((member) => member.userID !== userID));
|
|
25
|
+
};
|
|
26
|
+
const handleClearAll = () => {
|
|
27
|
+
onChangeSelectedMembers([]);
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("create_group.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }) }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("create_group.no_member_found") })) : (accounts.map((account) => {
|
|
30
|
+
const isSelected = selectedIds.has(account.userID);
|
|
31
|
+
return (_jsxs("div", { className: clsx("flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 transition-colors", isSelected ? "bg-primary-50" : "hover:bg-gray-50"), onClick: () => toggleMember(account), children: [_jsx(Avatar, { src: account.avatar, size: 40, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), _jsx("span", { className: clsx("flex h-4 w-4 shrink-0 items-center justify-center rounded border", isSelected
|
|
32
|
+
? "border-primary-400 bg-primary-400"
|
|
33
|
+
: "border-gray-300 bg-white"), children: isSelected && (_jsx(Icon, { icon: "check-b", size: 11, className: "text-white" })) })] }, account.userID));
|
|
34
|
+
})) })] }), _jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsxs("div", { className: "flex items-center justify-between px-1", children: [_jsx("span", { className: "text-sm text-gray-500", children: t("create_group.selected_count", { count: selectedMembers.length }) }), selectedMembers.length > 0 && (_jsx("button", { type: "button", className: "text-sm text-primary-400 hover:text-primary-500 hover:underline", onClick: handleClearAll, children: t("create_group.clear_all") }))] }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: selectedMembers.length === 0 ? (_jsxs("div", { className: "flex h-full min-h-[200px] flex-col items-center justify-center gap-2 text-center", children: [_jsx(Icon, { icon: "user-three-b", size: 28, className: "text-gray-300" }), _jsx("span", { className: "text-sm text-gray-400", children: t("create_group.no_member_selected") })] })) : (selectedMembers.map((member) => (_jsxs("div", { className: "flex items-center gap-3 rounded-lg px-3 py-3", children: [_jsx(Avatar, { src: member.avatar, size: 40, className: "shrink-0", children: member.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: member.displayName }), _jsx(Icon, { icon: "close-b", size: 14, className: "shrink-0 cursor-pointer text-gray-400", onClick: () => handleRemoveMember(member.userID) })] }, member.userID)))) })] })] }));
|
|
35
|
+
};
|
|
36
|
+
export default GroupMemberPicker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskAssignedSession.d.ts","sourceRoot":"","sources":["../../../src/components/session/DeskAssignedSession.tsx"],"names":[],"mappings":"AAeA,QAAA,MAAM,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"DeskAssignedSession.d.ts","sourceRoot":"","sources":["../../../src/components/session/DeskAssignedSession.tsx"],"names":[],"mappings":"AAeA,QAAA,MAAM,mBAAmB,+CAoFxB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -16,8 +16,11 @@ const DeskAssignedSession = () => {
|
|
|
16
16
|
const [collapsed, { toggle }] = useBoolean(false);
|
|
17
17
|
const setFilterSummary = useSessionStore((state) => state.setFilterSummary);
|
|
18
18
|
const filterSummary = useSessionStore((state) => state.filterSummary);
|
|
19
|
+
const viewMode = useSessionStore((state) => state.viewMode);
|
|
20
|
+
const setViewMode = useSessionStore((state) => state.setViewMode);
|
|
19
21
|
const { data: sessionSummary, refetch } = useGetSessionSummary();
|
|
20
22
|
const onFilterSelect = useCallback((filter) => setFilterSummary(filter), [setFilterSummary]);
|
|
23
|
+
const onAllGroupsSelect = useCallback(() => setViewMode("allGroups"), [setViewMode]);
|
|
21
24
|
useEffect(() => {
|
|
22
25
|
const handler = () => refetch();
|
|
23
26
|
emitter.on("UPDATE_SESSION", handler);
|
|
@@ -28,6 +31,6 @@ const DeskAssignedSession = () => {
|
|
|
28
31
|
if (collapsed) {
|
|
29
32
|
return (_jsx("div", { className: "flex flex-col items-center pt-4 border-r border-gray-200 bg-white", children: _jsx(Button, { type: "text", shape: "circle", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "angle-right-o", size: 22 }) }) }));
|
|
30
33
|
}
|
|
31
|
-
return (_jsx(Sider, { collapsible: true, collapsed: false, width: 220, className: "bg-white h-full border-r border-gray-200", trigger: null, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b shrink-0", children: [_jsx("span", { className: "text-md font-semibold flex-1 truncate", children: "Droppii Livechat" }), _jsx(Button, { type: "text", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "angle-left-o", size: 22 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto min-h-0", children: [_jsx("div", { className: "px-4 pt-4 pb-2 text-xs font-semibold text-gray-400 tracking-wider", children: t("my_messages") }), sessionSummary && (_jsx(SessionFilterMenu, { summary: sessionSummary, isActive: !filterSummary.teamId, filterSummary: filterSummary, onFilterSelect: onFilterSelect, activeCount: sessionSummary.activeSessionCount })), _jsx(DeskTeamInbox, {})] })] }) }));
|
|
34
|
+
return (_jsx(Sider, { collapsible: true, collapsed: false, width: 220, className: "bg-white h-full border-r border-gray-200", trigger: null, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b shrink-0", children: [_jsx("span", { className: "text-md font-semibold flex-1 truncate", children: "Droppii Livechat" }), _jsx(Button, { type: "text", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "angle-left-o", size: 22 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto min-h-0", children: [_jsx("div", { className: "px-4 pt-4 pb-2 text-xs font-semibold text-gray-400 tracking-wider", children: t("my_messages") }), sessionSummary && (_jsx(SessionFilterMenu, { summary: sessionSummary, isActive: !filterSummary.teamId && viewMode === "sessions", isAllGroupsActive: viewMode === "allGroups", filterSummary: filterSummary, onFilterSelect: onFilterSelect, onAllGroupsSelect: onAllGroupsSelect, activeCount: sessionSummary.activeSessionCount })), _jsx(DeskTeamInbox, {})] })] }) }));
|
|
32
35
|
};
|
|
33
36
|
export default DeskAssignedSession;
|
|
@@ -3,11 +3,13 @@ import { ISessionSummaryResponse } from "../../types/dto";
|
|
|
3
3
|
interface SessionFilterMenuProps {
|
|
4
4
|
summary: ISessionSummaryResponse;
|
|
5
5
|
isActive: boolean;
|
|
6
|
+
isAllGroupsActive?: boolean;
|
|
6
7
|
filterSummary: IFilterSummary;
|
|
7
8
|
onFilterSelect: (filter: IFilterSummary) => void;
|
|
9
|
+
onAllGroupsSelect?: () => void;
|
|
8
10
|
includeUnassigned?: boolean;
|
|
9
11
|
activeCount?: number;
|
|
10
12
|
}
|
|
11
|
-
declare const SessionFilterMenu: ({ summary, isActive, filterSummary, onFilterSelect, includeUnassigned, activeCount, }: SessionFilterMenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare const SessionFilterMenu: ({ summary, isActive, isAllGroupsActive, filterSummary, onFilterSelect, onAllGroupsSelect, includeUnassigned, activeCount, }: SessionFilterMenuProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
14
|
export default SessionFilterMenu;
|
|
13
15
|
//# sourceMappingURL=SessionFilterMenu.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionFilterMenu.d.ts","sourceRoot":"","sources":["../../../src/components/session/SessionFilterMenu.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtD,UAAU,sBAAsB;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,cAAc,CAAC;IAC9B,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"SessionFilterMenu.d.ts","sourceRoot":"","sources":["../../../src/components/session/SessionFilterMenu.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtD,UAAU,sBAAsB;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,cAAc,CAAC;IAC9B,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAeD,QAAA,MAAM,iBAAiB,GAAI,6HASxB,sBAAsB,4CAkCxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -4,8 +4,10 @@ import { useMemo } from "react";
|
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import { Menu } from "antd";
|
|
6
6
|
import { SessionStatus } from "../../types/chat";
|
|
7
|
-
import { buildSessionMenuItems } from "./sessionMenuItems";
|
|
8
|
-
const getSelectedKeys = (isActive, filterSummary) => {
|
|
7
|
+
import { ALL_GROUPS_KEY, buildSessionMenuItems } from "./sessionMenuItems";
|
|
8
|
+
const getSelectedKeys = (isActive, isAllGroupsActive, filterSummary) => {
|
|
9
|
+
if (isAllGroupsActive)
|
|
10
|
+
return [ALL_GROUPS_KEY];
|
|
9
11
|
if (!isActive)
|
|
10
12
|
return [];
|
|
11
13
|
if (filterSummary.status === SessionStatus.COMPLETED)
|
|
@@ -13,15 +15,16 @@ const getSelectedKeys = (isActive, filterSummary) => {
|
|
|
13
15
|
const key = filterSummary.status || filterSummary.tag;
|
|
14
16
|
return key ? [key] : [];
|
|
15
17
|
};
|
|
16
|
-
const SessionFilterMenu = ({ summary, isActive, filterSummary, onFilterSelect, includeUnassigned = false, activeCount, }) => {
|
|
18
|
+
const SessionFilterMenu = ({ summary, isActive, isAllGroupsActive = false, filterSummary, onFilterSelect, onAllGroupsSelect, includeUnassigned = false, activeCount, }) => {
|
|
17
19
|
const { t } = useTranslation();
|
|
18
|
-
const selectedKeys = useMemo(() => getSelectedKeys(isActive, filterSummary), [isActive, filterSummary]);
|
|
20
|
+
const selectedKeys = useMemo(() => getSelectedKeys(isActive, isAllGroupsActive, filterSummary), [isActive, isAllGroupsActive, filterSummary]);
|
|
19
21
|
const menuItems = useMemo(() => buildSessionMenuItems({
|
|
20
22
|
summary,
|
|
21
23
|
t,
|
|
22
24
|
onFilterSelect,
|
|
25
|
+
onAllGroupsSelect,
|
|
23
26
|
includeUnassigned,
|
|
24
|
-
}), [summary, t, onFilterSelect, includeUnassigned]);
|
|
27
|
+
}), [summary, t, onFilterSelect, onAllGroupsSelect, includeUnassigned]);
|
|
25
28
|
return (_jsx(Menu, { selectedKeys: selectedKeys, defaultOpenKeys: ["ACTIVE_SESSIONS"], mode: "inline", items: menuItems, inlineIndent: 12, expandIcon: activeCount ? (_jsx("span", { className: "text-xs", children: activeCount })) : undefined }));
|
|
26
29
|
};
|
|
27
30
|
export default SessionFilterMenu;
|
|
@@ -15,12 +15,14 @@ interface SessionFilterConfig {
|
|
|
15
15
|
}
|
|
16
16
|
export declare const SESSION_FILTER_CONFIGS: SessionFilterConfig[];
|
|
17
17
|
export declare const getFilterConfig: (key: string) => SessionFilterConfig | undefined;
|
|
18
|
+
export declare const ALL_GROUPS_KEY = "ALL_GROUPS";
|
|
18
19
|
interface BuildSessionMenuItemsOptions {
|
|
19
20
|
summary: ISessionSummaryResponse;
|
|
20
21
|
t: TFunction;
|
|
21
22
|
onFilterSelect: (filter: IFilterSummary) => void;
|
|
23
|
+
onAllGroupsSelect?: () => void;
|
|
22
24
|
includeUnassigned?: boolean;
|
|
23
25
|
}
|
|
24
|
-
export declare const buildSessionMenuItems: ({ summary, t, onFilterSelect, includeUnassigned, }: BuildSessionMenuItemsOptions) => MenuItem[];
|
|
26
|
+
export declare const buildSessionMenuItems: ({ summary, t, onFilterSelect, onAllGroupsSelect, includeUnassigned, }: BuildSessionMenuItemsOptions) => MenuItem[];
|
|
25
27
|
export {};
|
|
26
28
|
//# sourceMappingURL=sessionMenuItems.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionMenuItems.d.ts","sourceRoot":"","sources":["../../../src/components/session/sessionMenuItems.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,KAAK,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,UAAU,mBAAmB;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,EA2DvD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,oCACQ,CAAC;AAEpD,UAAU,4BAA4B;IACpC,OAAO,EAAE,uBAAuB,CAAC;IACjC,CAAC,EAAE,SAAS,CAAC;IACb,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA0BD,eAAO,MAAM,qBAAqB,GAAI,
|
|
1
|
+
{"version":3,"file":"sessionMenuItems.d.ts","sourceRoot":"","sources":["../../../src/components/session/sessionMenuItems.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,KAAK,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,UAAU,mBAAmB;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,EA2DvD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,oCACQ,CAAC;AAEpD,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,UAAU,4BAA4B;IACpC,OAAO,EAAE,uBAAuB,CAAC;IACjC,CAAC,EAAE,SAAS,CAAC;IACb,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA0BD,eAAO,MAAM,qBAAqB,GAAI,uEAMnC,4BAA4B,KAAG,QAAQ,EAsDzC,CAAC"}
|
|
@@ -62,6 +62,7 @@ export const SESSION_FILTER_CONFIGS = [
|
|
|
62
62
|
},
|
|
63
63
|
];
|
|
64
64
|
export const getFilterConfig = (key) => SESSION_FILTER_CONFIGS.find((c) => c.key === key);
|
|
65
|
+
export const ALL_GROUPS_KEY = "ALL_GROUPS";
|
|
65
66
|
const countBadge = (count, highlighted) => {
|
|
66
67
|
if (!count)
|
|
67
68
|
return null;
|
|
@@ -77,7 +78,7 @@ const getCount = (config, summary) => {
|
|
|
77
78
|
}
|
|
78
79
|
return ((_d = (_c = summary.tagCounts) === null || _c === void 0 ? void 0 : _c.find((s) => s.type === config.key)) === null || _d === void 0 ? void 0 : _d.count) || "";
|
|
79
80
|
};
|
|
80
|
-
export const buildSessionMenuItems = ({ summary, t, onFilterSelect, includeUnassigned = false, }) => {
|
|
81
|
+
export const buildSessionMenuItems = ({ summary, t, onFilterSelect, onAllGroupsSelect, includeUnassigned = false, }) => {
|
|
81
82
|
const activeConfigs = SESSION_FILTER_CONFIGS.filter((c) => c.key !== SessionStatus.COMPLETED &&
|
|
82
83
|
(!c.teamOnly || includeUnassigned));
|
|
83
84
|
const activeChildren = activeConfigs.map((config) => ({
|
|
@@ -97,6 +98,14 @@ export const buildSessionMenuItems = ({ summary, t, onFilterSelect, includeUnass
|
|
|
97
98
|
itemIcon: countBadge(summary.completedSessionCount),
|
|
98
99
|
}
|
|
99
100
|
: null;
|
|
101
|
+
const allGroupsItem = onAllGroupsSelect
|
|
102
|
+
? {
|
|
103
|
+
label: t("all_groups"),
|
|
104
|
+
key: ALL_GROUPS_KEY,
|
|
105
|
+
icon: _jsx(Icon, { icon: "user-three-o", size: 20 }),
|
|
106
|
+
onClick: onAllGroupsSelect,
|
|
107
|
+
}
|
|
108
|
+
: null;
|
|
100
109
|
return [
|
|
101
110
|
{
|
|
102
111
|
label: t("active_sessions"),
|
|
@@ -105,5 +114,6 @@ export const buildSessionMenuItems = ({ summary, t, onFilterSelect, includeUnass
|
|
|
105
114
|
children: activeChildren,
|
|
106
115
|
},
|
|
107
116
|
closedItem,
|
|
117
|
+
allGroupsItem,
|
|
108
118
|
].filter(Boolean);
|
|
109
119
|
};
|
|
@@ -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":"AAuCA,eAAO,MAAM,cAAc,YAiW1B,CAAC"}
|
|
@@ -201,6 +201,16 @@ export const useGlobalEvent = () => {
|
|
|
201
201
|
void refreshCurrentGroupState();
|
|
202
202
|
emitter.emit("GROUP_MEMBERS_CHANGED");
|
|
203
203
|
};
|
|
204
|
+
// createGroup()'s own response resolving is not enough to guarantee the
|
|
205
|
+
// new group shows up in conversationList — OnConversationChanged/
|
|
206
|
+
// OnNewConversation for it can arrive after an unspecified delay (per
|
|
207
|
+
// OpenIM docs: "promise completion, event delivery, and reconciliation
|
|
208
|
+
// query are separate stages"). OnJoinedGroupAdded fires as soon as the
|
|
209
|
+
// client is confirmed a member, so refetch here instead of waiting on the
|
|
210
|
+
// conversation-side events to eventually catch up.
|
|
211
|
+
const joinedGroupAddedHandler = (_event) => {
|
|
212
|
+
loadConversationList();
|
|
213
|
+
};
|
|
204
214
|
const setIMListener = () => {
|
|
205
215
|
//account
|
|
206
216
|
DChatSDK.on(CbEvents.OnUserTokenExpired, userTokenHandler);
|
|
@@ -223,6 +233,7 @@ export const useGlobalEvent = () => {
|
|
|
223
233
|
// group members
|
|
224
234
|
DChatSDK.on(CbEvents.OnGroupMemberAdded, groupMemberChangedHandler);
|
|
225
235
|
DChatSDK.on(CbEvents.OnGroupMemberDeleted, groupMemberChangedHandler);
|
|
236
|
+
DChatSDK.on(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
|
|
226
237
|
//busines notification
|
|
227
238
|
DChatSDK.on(CbEvents.OnRecvCustomBusinessMessage, businessNotificationHandler);
|
|
228
239
|
};
|
|
@@ -248,6 +259,7 @@ export const useGlobalEvent = () => {
|
|
|
248
259
|
// group members
|
|
249
260
|
DChatSDK.off(CbEvents.OnGroupMemberAdded, groupMemberChangedHandler);
|
|
250
261
|
DChatSDK.off(CbEvents.OnGroupMemberDeleted, groupMemberChangedHandler);
|
|
262
|
+
DChatSDK.off(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
|
|
251
263
|
//busines notification
|
|
252
264
|
DChatSDK.off(CbEvents.OnRecvCustomBusinessMessage, businessNotificationHandler);
|
|
253
265
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EligibleAccountItem } from "./useEligibleAccounts";
|
|
2
|
+
interface CreateGroupParams {
|
|
3
|
+
groupName: string;
|
|
4
|
+
isPublic: boolean;
|
|
5
|
+
members: EligibleAccountItem[];
|
|
6
|
+
faceURL?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const useCreateGroup: () => {
|
|
9
|
+
createGroup: ({ groupName, isPublic, members, faceURL }: CreateGroupParams) => Promise<import("@droppii-org/wasm-client-sdk").ConversationItem>;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=useCreateGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIjE,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,cAAc;6DAO2B,iBAAiB;CAoDtE,CAAC"}
|