@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.
Files changed (56) hide show
  1. package/dist/components/compose/ComposeNewMessageModal.d.ts +7 -0
  2. package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -0
  3. package/dist/components/compose/ComposeNewMessageModal.js +61 -0
  4. package/dist/components/conversation/ConversationByGroupItem.d.ts +7 -0
  5. package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -0
  6. package/dist/components/conversation/ConversationByGroupItem.js +50 -0
  7. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  8. package/dist/components/conversation/DeskConversationList.js +15 -6
  9. package/dist/components/group/CreateGroupForm.d.ts +7 -0
  10. package/dist/components/group/CreateGroupForm.d.ts.map +1 -0
  11. package/dist/components/group/CreateGroupForm.js +74 -0
  12. package/dist/components/group/GroupMemberPicker.d.ts +8 -0
  13. package/dist/components/group/GroupMemberPicker.d.ts.map +1 -0
  14. package/dist/components/group/GroupMemberPicker.js +36 -0
  15. package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
  16. package/dist/components/session/DeskAssignedSession.js +4 -1
  17. package/dist/components/session/SessionFilterMenu.d.ts +3 -1
  18. package/dist/components/session/SessionFilterMenu.d.ts.map +1 -1
  19. package/dist/components/session/SessionFilterMenu.js +8 -5
  20. package/dist/components/session/sessionMenuItems.d.ts +3 -1
  21. package/dist/components/session/sessionMenuItems.d.ts.map +1 -1
  22. package/dist/components/session/sessionMenuItems.js +11 -1
  23. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  24. package/dist/hooks/global/useGlobalEvent.js +12 -0
  25. package/dist/hooks/group/useCreateGroup.d.ts +12 -0
  26. package/dist/hooks/group/useCreateGroup.d.ts.map +1 -0
  27. package/dist/hooks/group/useCreateGroup.js +62 -0
  28. package/dist/hooks/group/useEligibleAccounts.d.ts +10 -0
  29. package/dist/hooks/group/useEligibleAccounts.d.ts.map +1 -0
  30. package/dist/hooks/group/useEligibleAccounts.js +23 -0
  31. package/dist/hooks/group/useGetGroupCandidates.d.ts +9 -0
  32. package/dist/hooks/group/useGetGroupCandidates.d.ts.map +1 -0
  33. package/dist/hooks/group/useGetGroupCandidates.js +20 -0
  34. package/dist/hooks/group/useJoinedGroups.d.ts +13 -0
  35. package/dist/hooks/group/useJoinedGroups.d.ts.map +1 -0
  36. package/dist/hooks/group/useJoinedGroups.js +33 -0
  37. package/dist/locales/vi/common.json +1 -0
  38. package/dist/locales/vi/conversation-inbox.json +27 -0
  39. package/dist/screens/desk-message/index.d.ts.map +1 -1
  40. package/dist/screens/desk-message/index.js +4 -1
  41. package/dist/services/query.d.ts +2 -0
  42. package/dist/services/query.d.ts.map +1 -1
  43. package/dist/services/query.js +2 -0
  44. package/dist/services/routes.d.ts +1 -0
  45. package/dist/services/routes.d.ts.map +1 -1
  46. package/dist/services/routes.js +1 -0
  47. package/dist/store/session.d.ts +3 -0
  48. package/dist/store/session.d.ts.map +1 -1
  49. package/dist/store/session.js +3 -1
  50. package/dist/styles/global.css +1 -1
  51. package/dist/types/dto.d.ts +12 -0
  52. package/dist/types/dto.d.ts.map +1 -1
  53. package/dist/utils/groupName.d.ts +8 -0
  54. package/dist/utils/groupName.d.ts.map +1 -0
  55. package/dist/utils/groupName.js +6 -0
  56. 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":"AAsBA,QAAA,MAAM,oBAAoB,+CAgNzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
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 = filterSummary.status || filterSummary.tag || "";
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 && (_jsx(Button, { onClick: onCloseSearch, variant: "outlined", className: "p-0 w-[36px] h-[36px] text-gray-500", children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] }), _jsxs("div", { className: "flex items-center justify-between mt-2", children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate", children: pathName }), filterConfig && (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: filterConfig.iconName, size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t(filterConfig.labelKey) })] }))] })] }), _jsxs("div", { id: "scrollableConversationsDiv", style: {
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,+CA2ExB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
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;AAaD,QAAA,MAAM,iBAAiB,GAAI,uFAOxB,sBAAsB,4CAiCxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
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,oDAKnC,4BAA4B,KAAG,QAAQ,EA4CzC,CAAC"}
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":"AAsCA,eAAO,MAAM,cAAc,YAoV1B,CAAC"}
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"}