@droppii-org/chat-sdk 0.1.97 → 0.2.0

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 (107) hide show
  1. package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
  2. package/dist/components/compose/ComposeNewMessageModal.js +1 -1
  3. package/dist/components/conversation/ThreadCard.js +1 -1
  4. package/dist/components/group/AddMemberModal.d.ts +9 -0
  5. package/dist/components/group/AddMemberModal.d.ts.map +1 -0
  6. package/dist/components/group/AddMemberModal.js +70 -0
  7. package/dist/components/group/AdminPermissionModal.d.ts +9 -0
  8. package/dist/components/group/AdminPermissionModal.d.ts.map +1 -0
  9. package/dist/components/group/AdminPermissionModal.js +63 -0
  10. package/dist/components/group/CreateGroupForm.d.ts +1 -2
  11. package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
  12. package/dist/components/group/CreateGroupForm.js +4 -4
  13. package/dist/components/group/GroupMemberPicker.d.ts.map +1 -1
  14. package/dist/components/group/GroupMemberPicker.js +16 -8
  15. package/dist/components/group/MemberPermissionPanel.d.ts +8 -0
  16. package/dist/components/group/MemberPermissionPanel.d.ts.map +1 -0
  17. package/dist/components/group/MemberPermissionPanel.js +53 -0
  18. package/dist/components/media-resources/FileResourceList.js +1 -1
  19. package/dist/components/media-resources/LinkResourceItem.js +1 -1
  20. package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
  21. package/dist/components/message/GroupMembersDrawer.js +210 -28
  22. package/dist/components/message/MessageHeader.d.ts +2 -1
  23. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  24. package/dist/components/message/MessageHeader.js +7 -2
  25. package/dist/components/message/MessageList.d.ts +1 -0
  26. package/dist/components/message/MessageList.d.ts.map +1 -1
  27. package/dist/components/message/MessageList.js +7 -2
  28. package/dist/components/message/SelectSession.d.ts.map +1 -1
  29. package/dist/components/message/SelectSession.js +3 -3
  30. package/dist/components/session/DeskTeamInbox.js +1 -1
  31. package/dist/components/thread/GroupThreadInfo.d.ts +11 -0
  32. package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -0
  33. package/dist/components/thread/GroupThreadInfo.js +156 -0
  34. package/dist/components/thread/ManualAssignPopover.js +2 -2
  35. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  36. package/dist/components/thread/SessionSection.js +2 -8
  37. package/dist/components/thread/SingleThreadInfo.d.ts +6 -0
  38. package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -0
  39. package/dist/components/thread/SingleThreadInfo.js +70 -0
  40. package/dist/components/thread/ThreadInfo.d.ts +7 -1
  41. package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
  42. package/dist/components/thread/ThreadInfo.js +14 -2
  43. package/dist/constants/index.d.ts +1 -0
  44. package/dist/constants/index.d.ts.map +1 -1
  45. package/dist/constants/index.js +3 -0
  46. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  47. package/dist/hooks/global/useGlobalEvent.js +27 -0
  48. package/dist/hooks/group/useAddGroupMember.d.ts +7 -0
  49. package/dist/hooks/group/useAddGroupMember.d.ts.map +1 -0
  50. package/dist/hooks/group/useAddGroupMember.js +20 -0
  51. package/dist/hooks/group/useDismissGroup.d.ts +4 -0
  52. package/dist/hooks/group/useDismissGroup.d.ts.map +1 -0
  53. package/dist/hooks/group/useDismissGroup.js +22 -0
  54. package/dist/hooks/group/useEligibleAccounts.d.ts +3 -0
  55. package/dist/hooks/group/useEligibleAccounts.d.ts.map +1 -1
  56. package/dist/hooks/group/useEligibleAccounts.js +9 -9
  57. package/dist/hooks/group/useEligibleAccountsForGroup.d.ts +9 -0
  58. package/dist/hooks/group/useEligibleAccountsForGroup.d.ts.map +1 -0
  59. package/dist/hooks/group/useEligibleAccountsForGroup.js +26 -0
  60. package/dist/hooks/group/useGetGroupCandidates.d.ts +7 -2
  61. package/dist/hooks/group/useGetGroupCandidates.d.ts.map +1 -1
  62. package/dist/hooks/group/useGetGroupCandidates.js +33 -16
  63. package/dist/hooks/group/useGroupMemberActions.d.ts +25 -0
  64. package/dist/hooks/group/useGroupMemberActions.d.ts.map +1 -0
  65. package/dist/hooks/group/useGroupMemberActions.js +54 -0
  66. package/dist/hooks/group/useGroupPermission.d.ts +15 -0
  67. package/dist/hooks/group/useGroupPermission.d.ts.map +1 -0
  68. package/dist/hooks/group/useGroupPermission.js +70 -0
  69. package/dist/hooks/group/useSearchGroupMembers.d.ts +8 -0
  70. package/dist/hooks/group/useSearchGroupMembers.d.ts.map +1 -0
  71. package/dist/hooks/group/useSearchGroupMembers.js +37 -0
  72. package/dist/hooks/group/useSetMemberPermission.d.ts +12 -0
  73. package/dist/hooks/group/useSetMemberPermission.d.ts.map +1 -0
  74. package/dist/hooks/group/useSetMemberPermission.js +32 -0
  75. package/dist/hooks/group/useToggleGroupSendMessage.d.ts +7 -0
  76. package/dist/hooks/group/useToggleGroupSendMessage.d.ts.map +1 -0
  77. package/dist/hooks/group/useToggleGroupSendMessage.js +22 -0
  78. package/dist/hooks/group/useUpdateGroupInfo.d.ts +7 -0
  79. package/dist/hooks/group/useUpdateGroupInfo.d.ts.map +1 -0
  80. package/dist/hooks/group/useUpdateGroupInfo.js +15 -0
  81. package/dist/hooks/session/useBlockUser.d.ts +10 -0
  82. package/dist/hooks/session/useBlockUser.d.ts.map +1 -0
  83. package/dist/hooks/session/useBlockUser.js +49 -0
  84. package/dist/hooks/session/useDeleteConversation.d.ts +4 -0
  85. package/dist/hooks/session/useDeleteConversation.d.ts.map +1 -0
  86. package/dist/hooks/session/useDeleteConversation.js +20 -0
  87. package/dist/hooks/session/useGetListSessionByConversation.d.ts +2 -1
  88. package/dist/hooks/session/useGetListSessionByConversation.d.ts.map +1 -1
  89. package/dist/hooks/session/useGetListSessionByConversation.js +2 -2
  90. package/dist/hooks/session/useToggleConversationNotification.d.ts +7 -0
  91. package/dist/hooks/session/useToggleConversationNotification.d.ts.map +1 -0
  92. package/dist/hooks/session/useToggleConversationNotification.js +15 -0
  93. package/dist/locales/vi/common.json +63 -1
  94. package/dist/locales/vi/conversation-inbox.json +6 -0
  95. package/dist/screens/desk-message/index.d.ts +3 -1
  96. package/dist/screens/desk-message/index.d.ts.map +1 -1
  97. package/dist/screens/desk-message/index.js +4 -5
  98. package/dist/services/query.d.ts +2 -0
  99. package/dist/services/query.d.ts.map +1 -1
  100. package/dist/services/query.js +2 -0
  101. package/dist/store/groupPermissionOverrides.d.ts +23 -0
  102. package/dist/store/groupPermissionOverrides.d.ts.map +1 -0
  103. package/dist/store/groupPermissionOverrides.js +12 -0
  104. package/dist/styles/global.css +1 -1
  105. package/dist/utils/events.d.ts +1 -0
  106. package/dist/utils/events.d.ts.map +1 -1
  107. package/package.json +2 -2
@@ -0,0 +1,156 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Avatar, Input, Modal, Switch, message } from "antd";
4
+ import { useTranslation } from "react-i18next";
5
+ import { GroupMemberRole, MessageReceiveOptType } from "@droppii-org/wasm-client-sdk";
6
+ import { Icon } from "../../components/icon";
7
+ import useConversationStore from "../../store/conversation";
8
+ import { useLeaveConversation } from "../../hooks/session/useLeaveConversation";
9
+ import { useDeleteConversation } from "../../hooks/session/useDeleteConversation";
10
+ import { useToggleConversationNotification } from "../../hooks/session/useToggleConversationNotification";
11
+ import { useUpdateGroupInfo } from "../../hooks/group/useUpdateGroupInfo";
12
+ import { useToggleGroupSendMessage } from "../../hooks/group/useToggleGroupSendMessage";
13
+ import { useDismissGroup } from "../../hooks/group/useDismissGroup";
14
+ import { useGroupPermission } from "../../hooks/group/useGroupPermission";
15
+ import AdminPermissionModal from "../../components/group/AdminPermissionModal";
16
+ import MemberPermissionPanel from "../../components/group/MemberPermissionPanel";
17
+ const GroupThreadInfo = ({ onClose, config }) => {
18
+ var _a, _b, _c, _d, _e;
19
+ const { t } = useTranslation();
20
+ const conversationData = useConversationStore((state) => state.conversationData);
21
+ const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
22
+ const groupID = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) || "";
23
+ const conversationID = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
24
+ const permissions = useGroupPermission(groupID);
25
+ const currentMemberInGroup = useConversationStore((state) => state.currentMemberInGroup);
26
+ // Muted-chat-list and disband-group aren't part of the 11-permission
27
+ // matrix in the spec — kept as a role check like before, not mocked
28
+ // per-permission.
29
+ const isAdminOrOwner = (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Admin ||
30
+ (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Owner;
31
+ const [editingField, setEditingField] = useState(null);
32
+ const [nameValue, setNameValue] = useState((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _a !== void 0 ? _a : "");
33
+ const [descriptionValue, setDescriptionValue] = useState((_b = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _b !== void 0 ? _b : "");
34
+ const [confirmAction, setConfirmAction] = useState(null);
35
+ const [activeRow, setActiveRow] = useState(null);
36
+ const [notifyEnabled, setNotifyEnabled] = useState((conversationData === null || conversationData === void 0 ? void 0 : conversationData.recvMsgOpt) !== MessageReceiveOptType.NotNotify);
37
+ // Q4 (non-block, pending BE/SDK confirmation): openIM's GroupItem has no
38
+ // readable "is send disabled" field for changeGroupMute — tracked locally
39
+ // until BE confirms where (or whether) this persists server-side.
40
+ const [allowSendMessage, setAllowSendMessage] = useState(true);
41
+ useEffect(() => {
42
+ setNotifyEnabled((conversationData === null || conversationData === void 0 ? void 0 : conversationData.recvMsgOpt) !== MessageReceiveOptType.NotNotify);
43
+ }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.recvMsgOpt]);
44
+ const { mutate: updateGroupInfo } = useUpdateGroupInfo();
45
+ const { mutate: toggleSendMessage, isPending: isTogglingSendMessage } = useToggleGroupSendMessage();
46
+ const { mutate: toggleNotification, isPending: isTogglingNotification } = useToggleConversationNotification();
47
+ const { mutateAsync: leaveConversation, isPending: isLeaving } = useLeaveConversation();
48
+ const { mutateAsync: dismissGroup, isPending: isDismissing } = useDismissGroup();
49
+ const { mutateAsync: deleteConversation, isPending: isDeleting } = useDeleteConversation();
50
+ const startEditName = () => {
51
+ var _a;
52
+ setNameValue((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _a !== void 0 ? _a : "");
53
+ setEditingField("name");
54
+ };
55
+ const startEditDescription = () => {
56
+ var _a;
57
+ setDescriptionValue((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _a !== void 0 ? _a : "");
58
+ setEditingField("description");
59
+ };
60
+ const saveName = () => {
61
+ const trimmed = nameValue.trim();
62
+ setEditingField(null);
63
+ if (!groupID || !trimmed || trimmed === (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName))
64
+ return;
65
+ updateGroupInfo({ groupID, groupName: trimmed }, { onError: () => message.error(t("update_group_info_failed")) });
66
+ };
67
+ const cancelEditDescription = () => {
68
+ var _a;
69
+ setDescriptionValue((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _a !== void 0 ? _a : "");
70
+ setEditingField(null);
71
+ };
72
+ const saveDescription = () => {
73
+ const trimmed = descriptionValue.trim();
74
+ setEditingField(null);
75
+ if (!groupID || trimmed === (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction))
76
+ return;
77
+ updateGroupInfo({ groupID, introduction: trimmed }, { onError: () => message.error(t("update_group_info_failed")) });
78
+ };
79
+ const handleToggleNotification = (checked) => {
80
+ if (!conversationID)
81
+ return;
82
+ const previous = notifyEnabled;
83
+ setNotifyEnabled(checked);
84
+ toggleNotification({ conversationID, notify: checked }, {
85
+ onError: () => {
86
+ setNotifyEnabled(previous);
87
+ message.error(t("update_group_info_failed"));
88
+ },
89
+ });
90
+ };
91
+ const handleToggleSendMessage = (checked) => {
92
+ if (!groupID)
93
+ return;
94
+ const previous = allowSendMessage;
95
+ setAllowSendMessage(checked);
96
+ toggleSendMessage({ groupID, allowSendMessage: checked }, {
97
+ onError: () => {
98
+ setAllowSendMessage(previous);
99
+ message.error(t("update_group_info_failed"));
100
+ },
101
+ });
102
+ };
103
+ const handleConfirmLeave = async () => {
104
+ var _a, _b;
105
+ if (!conversationID)
106
+ return;
107
+ try {
108
+ await leaveConversation({ conversationId: conversationID });
109
+ message.success(t("leave_group_success"));
110
+ onClose === null || onClose === void 0 ? void 0 : onClose();
111
+ }
112
+ catch (error) {
113
+ message.error(((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || t("leave_group_failed"));
114
+ }
115
+ finally {
116
+ setConfirmAction(null);
117
+ }
118
+ };
119
+ const handleConfirmDisband = async () => {
120
+ var _a, _b;
121
+ if (!groupID)
122
+ return;
123
+ try {
124
+ await dismissGroup({ groupID });
125
+ message.success(t("disband_group_success"));
126
+ onClose === null || onClose === void 0 ? void 0 : onClose();
127
+ }
128
+ catch (error) {
129
+ message.error(((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || t("disband_group_failed"));
130
+ }
131
+ finally {
132
+ setConfirmAction(null);
133
+ }
134
+ };
135
+ const handleConfirmDelete = async () => {
136
+ var _a, _b;
137
+ if (!conversationID)
138
+ return;
139
+ try {
140
+ await deleteConversation({ conversationID });
141
+ message.success(t("delete_conversation_success"));
142
+ onClose === null || onClose === void 0 ? void 0 : onClose();
143
+ }
144
+ catch (error) {
145
+ message.error(((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || t("delete_conversation_failed"));
146
+ }
147
+ finally {
148
+ setConfirmAction(null);
149
+ }
150
+ };
151
+ if (!groupID) {
152
+ return null;
153
+ }
154
+ return (_jsxs("div", { className: "h-full w-[330px] bg-white shadow-md flex flex-col relative overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-4 border-b border-gray-100", children: [_jsx("span", { className: "text-lg font-medium", children: t("thread_info_title") }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 rounded-full text-gray-500 hover:bg-gray-100", onClick: onClose, children: _jsx(Icon, { icon: "close-o", size: 20 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-4", children: [_jsxs("div", { className: "relative shrink-0", children: [_jsx(Avatar, { src: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.faceURL, size: 56, children: ((_d = (_c = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) === null || _c === void 0 ? void 0 : _c.charAt) === null || _d === void 0 ? void 0 : _d.call(_c, 0)) || "G" }), permissions.updateGroupAvatar && (_jsx("span", { className: "absolute -bottom-1 -right-1 flex items-center justify-center w-5 h-5 rounded-full bg-black text-white", children: _jsx(Icon, { icon: "camera-o", size: 12 }) }))] }), _jsx("div", { className: "flex-1 min-w-0", children: editingField === "name" ? (_jsx(Input, { autoFocus: true, value: nameValue, onChange: (event) => setNameValue(event.target.value), onPressEnter: saveName, onBlur: saveName, maxLength: 100 })) : (_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("p", { className: "text-base font-medium truncate", children: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName }), permissions.updateGroupName && (_jsx(Icon, { icon: "edit-o", size: 16, className: "shrink-0 cursor-pointer text-gray-500", onClick: startEditName }))] })) })] }), _jsx("div", { className: "px-4 pb-4", children: editingField === "description" ? (_jsxs("div", { className: "flex flex-col gap-3 bg-[#f5f5f5] rounded-lg p-3", children: [_jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-gray-900", children: [_jsx(Icon, { icon: "info-circle-o", size: 18, className: "text-gray-500" }), t("description")] }), _jsx(Input.TextArea, { autoFocus: true, value: descriptionValue, onChange: (event) => setDescriptionValue(event.target.value), placeholder: t("enter_group_description"), autoSize: { minRows: 1, maxRows: 4 }, maxLength: 500, className: "bg-white" }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full border border-primary-500 text-primary-500 text-sm font-medium", onClick: cancelEditDescription, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full text-primary-500 text-sm font-medium disabled:text-gray-300", disabled: descriptionValue.trim() === ((_e = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _e !== void 0 ? _e : ""), onClick: saveDescription, children: t("save") })] })] })) : (_jsxs("div", { className: "flex items-start justify-between gap-2 bg-[#f5f5f5] rounded-lg px-3 py-2.5", children: [_jsxs("div", { className: "flex items-start gap-2 flex-1 min-w-0", children: [_jsx(Icon, { icon: "info-circle-o", size: 18, className: "shrink-0 mt-0.5 text-gray-900" }), _jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: t("description") }), _jsx("p", { className: "text-sm text-gray-400 truncate", children: (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) || t("no_description") })] })] }), permissions.updateGroupDescription && (_jsx(Icon, { icon: "edit-o", size: 16, className: "shrink-0 cursor-pointer text-gray-500", onClick: startEditDescription }))] })) }), _jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 rounded-lg overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-3", children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "bell-o", size: 18 }), t("notification")] }), _jsx(Switch, { checked: notifyEnabled, onChange: handleToggleNotification, loading: isTogglingNotification })] }), (config === null || config === void 0 ? void 0 : config.showAllowSendMessage) && (_jsxs("div", { className: "flex items-center justify-between px-3 py-3", children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "user-check-o", size: 18 }), t("allow_send_message")] }), _jsx(Switch, { checked: allowSendMessage, onChange: handleToggleSendMessage, loading: isTogglingSendMessage })] })), isAdminOrOwner && (config === null || config === void 0 ? void 0 : config.showMutedChatList) && (_jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("muted-chat-list"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "bell-off-o", size: 18 }), t("muted_chat_list")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] })), _jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("block-list"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "user-del-o", size: 18 }), t("block_list")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] })] }), permissions.configGroupPermission && (_jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 mt-3 rounded-lg overflow-hidden", children: [_jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("member-permission"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "shield-o", size: 18 }), t("member_permission_settings")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] }), _jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("admin-permission"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "shield-done-o", size: 18 }), t("admin_permission_settings")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] })] })), _jsx("div", { className: "mx-4 mt-3 rounded-lg overflow-hidden bg-[#f5f5f5]", children: _jsxs("button", { type: "button", className: "w-full flex items-center gap-3 px-3 py-3 text-left text-sm text-gray-400 cursor-not-allowed", disabled: true, title: t("invite_via_link_pending"), children: [_jsx(Icon, { icon: "link-alt-o", size: 18 }), t("invite_via_link")] }) }), _jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 mt-3 mb-4 rounded-lg overflow-hidden", children: [_jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("delete"), children: [_jsx(Icon, { icon: "trash-o", size: 18 }), t("delete_conversation")] }), _jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("leave"), children: [_jsx(Icon, { icon: "logout-o", size: 18 }), t("leave_group")] }), isAdminOrOwner && (_jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("disband"), children: [_jsx(Icon, { icon: "user-del-o", size: 18 }), t("disband_group")] }))] })] }), _jsx(Modal, { centered: true, open: confirmAction === "leave", onOk: handleConfirmLeave, onCancel: () => setConfirmAction(null), title: t("leave_group_confirm_title"), okText: t("leave_group"), cancelText: t("cancel"), okType: "danger", confirmLoading: isLeaving, getContainer: false, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(Modal, { centered: true, open: confirmAction === "disband", onOk: handleConfirmDisband, onCancel: () => setConfirmAction(null), title: t("disband_group_confirm_title"), okText: t("disband_group"), cancelText: t("cancel"), okType: "danger", confirmLoading: isDismissing, getContainer: false, children: _jsx("p", { children: t("disband_group_confirm_message") }) }), _jsx(Modal, { centered: true, open: confirmAction === "delete", onOk: handleConfirmDelete, onCancel: () => setConfirmAction(null), title: t("delete_conversation_confirm_title"), okText: t("delete_conversation"), cancelText: t("cancel"), okType: "danger", confirmLoading: isDeleting, getContainer: false, children: _jsx("p", { children: t("delete_conversation_confirm_message") }) }), _jsx(MemberPermissionPanel, { open: activeRow === "member-permission", groupID: groupID, onClose: () => setActiveRow(null) }), _jsx(AdminPermissionModal, { open: activeRow === "admin-permission", groupID: groupID, onClose: () => setActiveRow(null), inline: true }), (activeRow === "muted-chat-list" || activeRow === "block-list") && (_jsx(Modal, { centered: true, open: true, footer: null, onCancel: () => setActiveRow(null), title: t(activeRow === "muted-chat-list" ? "muted_chat_list" : "block_list"), getContainer: false, children: _jsx("p", { className: "text-sm text-gray-500", children: t("feature_coming_soon") }) }))] }));
155
+ };
156
+ export default GroupThreadInfo;
@@ -15,7 +15,7 @@ const SupporterItem = ({ supporter, onClick, isCurrentUser, }) => {
15
15
  var _a;
16
16
  const { t } = useTranslation();
17
17
  const isOnline = supporter.status === "ONLINE";
18
- return (_jsxs("div", { role: "option", tabIndex: 0, className: "flex items-center gap-3 px-3 py-2 rounded-md transition-colors cursor-pointer hover:bg-gray-50", onClick: () => onClick(supporter), children: [_jsx(Badge, { dot: true, color: isOnline ? "#52c41a" : "#d9d9d9", offset: [-2, 24], children: _jsx(Avatar, { src: supporter.avatar, size: 32, children: ((_a = supporter.displayName) === null || _a === void 0 ? void 0 : _a.charAt(0)) || "A" }) }), _jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [_jsx("span", { className: "text-sm truncate", children: supporter.displayName }), isCurrentUser && (_jsx("span", { className: "text-xs text-blue-500 shrink-0", children: t("current_account") }))] }), _jsx("div", { className: "flex items-center gap-2 shrink-0", children: !!supporter.openSessionCount && supporter.openSessionCount > 0 && (_jsxs("span", { className: "flex items-center gap-0.5 bg-blue-500 text-white text-xs font-medium px-1.5 py-0.5 rounded-full", children: [_jsx(Icon, { icon: "chat-square-b", size: 11 }), supporter.openSessionCount] })) })] }));
18
+ return (_jsxs("div", { role: "option", tabIndex: 0, className: "flex items-center gap-3 px-3 py-2 rounded-md transition-colors cursor-pointer hover:bg-[#f5f5f5]", onClick: () => onClick(supporter), children: [_jsx(Badge, { dot: true, color: isOnline ? "#52c41a" : "#d9d9d9", offset: [-2, 24], children: _jsx(Avatar, { src: supporter.avatar, size: 32, children: ((_a = supporter.displayName) === null || _a === void 0 ? void 0 : _a.charAt(0)) || "A" }) }), _jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [_jsx("span", { className: "text-sm truncate", children: supporter.displayName }), isCurrentUser && (_jsx("span", { className: "text-xs text-blue-500 shrink-0", children: t("current_account") }))] }), _jsx("div", { className: "flex items-center gap-2 shrink-0", children: !!supporter.openSessionCount && supporter.openSessionCount > 0 && (_jsxs("span", { className: "flex items-center gap-0.5 bg-blue-500 text-white text-xs font-medium px-1.5 py-0.5 rounded-full", children: [_jsx(Icon, { icon: "chat-square-b", size: 11 }), supporter.openSessionCount] })) })] }));
19
19
  };
20
20
  const ManualAssignPopover = ({ sessionId, supporter, teamId: propTeamId, children, excludeCurrentUser = false, }) => {
21
21
  var _a, _b;
@@ -94,7 +94,7 @@ const ManualAssignPopover = ({ sessionId, supporter, teamId: propTeamId, childre
94
94
  };
95
95
  const popoverContent = (_jsxs("div", { className: "w-64", children: [_jsxs("button", { type: "button", className: clsx("flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm transition-colors", isAutoAssignDisabled
96
96
  ? "cursor-not-allowed text-gray-400"
97
- : "text-gray-700 hover:bg-gray-50"), onClick: handleAutoAssign, disabled: isAutoAssignDisabled, children: [isAutoAssigning ? (_jsx(Spin, { size: "small" })) : (_jsx("span", { className: "text-blue-500", children: _jsx(Icon, { icon: "refresh-b", size: 20 }) })), _jsx("span", { children: t("manual_assign_button_auto_reassign") })] }), _jsx(Divider, { className: "my-2" }), _jsx("div", { className: "mb-2", children: _jsx(Input, { autoFocus: true, placeholder: t("manual_assign_search_placeholder"), value: searchText, onChange: (e) => setSearchText(e.target.value), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }), className: "rounded-lg border-gray-300 focus-within:border-chat-focus focus-within:shadow-chat-focus-ring", allowClear: true }) }), _jsx("div", { className: "max-h-72 overflow-y-auto", role: "listbox", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Spin, {}) })) : supporters && supporters.length > 0 ? (_jsx("div", { className: clsx("flex flex-col transition-opacity duration-200", isFetching && "opacity-50 pointer-events-none"), children: [...supporters]
97
+ : "text-gray-700 hover:bg-[#f5f5f5]"), onClick: handleAutoAssign, disabled: isAutoAssignDisabled, children: [isAutoAssigning ? (_jsx(Spin, { size: "small" })) : (_jsx("span", { className: "text-blue-500", children: _jsx(Icon, { icon: "refresh-b", size: 20 }) })), _jsx("span", { children: t("manual_assign_button_auto_reassign") })] }), _jsx(Divider, { className: "my-2" }), _jsx("div", { className: "mb-2", children: _jsx(Input, { autoFocus: true, placeholder: t("manual_assign_search_placeholder"), value: searchText, onChange: (e) => setSearchText(e.target.value), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }), className: "rounded-lg border-gray-300 focus-within:border-chat-focus focus-within:shadow-chat-focus-ring", allowClear: true }) }), _jsx("div", { className: "max-h-72 overflow-y-auto", role: "listbox", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Spin, {}) })) : supporters && supporters.length > 0 ? (_jsx("div", { className: clsx("flex flex-col transition-opacity duration-200", isFetching && "opacity-50 pointer-events-none"), children: [...supporters]
98
98
  .filter((item) => item.memberId !== (supporter === null || supporter === void 0 ? void 0 : supporter.id))
99
99
  .filter((item) => !excludeCurrentUser || item.memberId !== authUserID)
100
100
  .sort((supporterA, supporterB) => {
@@ -1 +1 @@
1
- {"version":3,"file":"SessionSection.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SessionSection.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAkB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqB/D,eAAO,MAAM,iBAAiB,GAAI,wBAG/B;IACD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;CACnB,4CAkNA,CAAC;AAiDF,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,yBAAyB,mBAAmB,4CAmDnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"SessionSection.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SessionSection.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAkB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqB/D,eAAO,MAAM,iBAAiB,GAAI,wBAG/B;IACD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;CACnB,4CAkNA,CAAC;AAiDF,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,yBAAyB,mBAAmB,4CAqCnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useBoolean, useDebounce } from "ahooks";
2
+ import { useDebounce } from "ahooks";
3
3
  import { Icon } from "../../components/icon";
4
4
  import { Button, Select, Spin, Tag, Input, message, Avatar } from "antd";
5
5
  import ManualAssignPopover from "./ManualAssignPopover";
@@ -133,16 +133,10 @@ const ClosedSessionItem = ({ session, expandedSessionId, onToggleExpand, }) => {
133
133
  };
134
134
  const SessionSection = ({ sessions, isLoading }) => {
135
135
  const { t } = useTranslation("common");
136
- const [isOpen, { toggle }] = useBoolean(true);
137
136
  const [expandedSessionId, setExpandedSessionId] = useState(null);
138
137
  const handleToggleExpand = (sessionId) => {
139
138
  setExpandedSessionId((prevId) => (prevId === sessionId ? null : sessionId));
140
139
  };
141
- useEffect(() => {
142
- if (!isOpen) {
143
- setExpandedSessionId(null);
144
- }
145
- }, [isOpen]);
146
- return (_jsxs("div", { className: "flex flex-col border-b", children: [_jsxs("div", { role: "button", onClick: toggle, className: "flex items-center justify-between px-4 py-2 rounded-md hover:bg-gray-100 sticky top-0 bg-white z-10", children: [_jsx("h3", { className: "font-bold text-gray-500 text-xs tracking-wider", children: t("sessions_heading") }), _jsx(Icon, { icon: isOpen ? "angle-up-o" : "angle-down-o", size: 18 })] }), isOpen && (_jsx("div", { className: "px-4 pt-2 pb-4", children: isLoading ? (_jsx("div", { className: "text-center mt-4", children: _jsx(Spin, {}) })) : (_jsx("div", { className: "flex flex-col gap-1", children: sessions.map((session) => (_jsx(ClosedSessionItem, { session: session, expandedSessionId: expandedSessionId, onToggleExpand: handleToggleExpand }, session.id))) })) }))] }));
140
+ return (_jsxs("div", { className: "flex flex-col border-b", children: [_jsx("div", { className: "flex items-center justify-between px-4 py-2 sticky top-0 bg-white z-10", children: _jsx("h3", { className: "font-bold text-gray-500 text-xs tracking-wider", children: t("sessions_heading") }) }), _jsx("div", { className: "px-4 pt-2 pb-4", children: isLoading ? (_jsx("div", { className: "text-center mt-4", children: _jsx(Spin, {}) })) : (_jsx("div", { className: "flex flex-col gap-1", children: sessions.map((session) => (_jsx(ClosedSessionItem, { session: session, expandedSessionId: expandedSessionId, onToggleExpand: handleToggleExpand }, session.id))) })) })] }));
147
141
  };
148
142
  export default SessionSection;
@@ -0,0 +1,6 @@
1
+ interface SingleThreadInfoProps {
2
+ onClose?: () => void;
3
+ }
4
+ declare const SingleThreadInfo: ({ onClose }: SingleThreadInfoProps) => import("react/jsx-runtime").JSX.Element | null;
5
+ export default SingleThreadInfo;
6
+ //# sourceMappingURL=SingleThreadInfo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SingleThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SingleThreadInfo.tsx"],"names":[],"mappings":"AAYA,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,aAAa,qBAAqB,mDAmJ3D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Avatar, Modal, Switch, message } from "antd";
4
+ import { useTranslation } from "react-i18next";
5
+ import { MessageReceiveOptType } from "@droppii-org/wasm-client-sdk";
6
+ import { Icon } from "../../components/icon";
7
+ import useConversationStore from "../../store/conversation";
8
+ import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
9
+ import { useIsUserBlocked, useBlockUser } from "../../hooks/session/useBlockUser";
10
+ import { useDeleteConversation } from "../../hooks/session/useDeleteConversation";
11
+ import { useToggleConversationNotification } from "../../hooks/session/useToggleConversationNotification";
12
+ import { PeerType } from "../../types/conversation";
13
+ const SingleThreadInfo = ({ onClose }) => {
14
+ var _a;
15
+ const { t } = useTranslation();
16
+ const conversationData = useConversationStore((state) => state.conversationData);
17
+ const { avatar, displayName } = useConversationDisplayData(conversationData);
18
+ const conversationID = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
19
+ const peerUserID = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.userID) || "";
20
+ const isBot = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.BOT;
21
+ const [confirmDelete, setConfirmDelete] = useState(false);
22
+ const [notifyEnabled, setNotifyEnabled] = useState((conversationData === null || conversationData === void 0 ? void 0 : conversationData.recvMsgOpt) !== MessageReceiveOptType.NotNotify);
23
+ const { isBlocked } = useIsUserBlocked(peerUserID);
24
+ const { mutate: toggleBlock, isPending: isTogglingBlock } = useBlockUser();
25
+ const { mutate: toggleNotification, isPending: isTogglingNotification } = useToggleConversationNotification();
26
+ const { mutateAsync: deleteConversation, isPending: isDeleting } = useDeleteConversation();
27
+ const handleToggleNotification = (checked) => {
28
+ if (!conversationID)
29
+ return;
30
+ const previous = notifyEnabled;
31
+ setNotifyEnabled(checked);
32
+ toggleNotification({ conversationID, notify: checked }, {
33
+ onError: () => {
34
+ setNotifyEnabled(previous);
35
+ message.error(t("update_group_info_failed"));
36
+ },
37
+ });
38
+ };
39
+ const handleToggleBlock = () => {
40
+ if (!peerUserID)
41
+ return;
42
+ toggleBlock({ userID: peerUserID, block: !isBlocked }, {
43
+ onSuccess: () => {
44
+ message.success(t(isBlocked ? "unblock_user_success" : "block_user_success"));
45
+ },
46
+ onError: () => message.error(t("block_user_failed")),
47
+ });
48
+ };
49
+ const handleConfirmDelete = async () => {
50
+ var _a, _b;
51
+ if (!conversationID)
52
+ return;
53
+ try {
54
+ await deleteConversation({ conversationID });
55
+ message.success(t("delete_conversation_success"));
56
+ onClose === null || onClose === void 0 ? void 0 : onClose();
57
+ }
58
+ catch (error) {
59
+ message.error(((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || t("delete_conversation_failed"));
60
+ }
61
+ finally {
62
+ setConfirmDelete(false);
63
+ }
64
+ };
65
+ if (!conversationID) {
66
+ return null;
67
+ }
68
+ return (_jsxs("div", { className: "h-full w-[330px] bg-white shadow-md flex flex-col relative", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-4 border-b border-gray-100", children: [_jsx("span", { className: "text-lg font-medium", children: t("thread_info_title") }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 rounded-full text-gray-500 hover:bg-gray-100", onClick: onClose, children: _jsx(Icon, { icon: "close-o", size: 20 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-4", children: [_jsx(Avatar, { src: avatar, size: 56, className: "shrink-0", children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), _jsxs("div", { className: "flex-1 min-w-0 flex items-center gap-2", children: [_jsx("p", { className: "text-base font-medium truncate", children: displayName }), isBot && (_jsx("span", { className: "shrink-0 rounded px-1.5 py-0.5 text-xs font-semibold bg-orange-50 text-orange-500", children: t("bot_tag") }))] })] }), _jsx("div", { className: "bg-[#f5f5f5] mx-4 rounded-lg overflow-hidden", children: _jsxs("div", { className: "flex items-center justify-between px-3 py-3", children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "bell-o", size: 18 }), t("notification")] }), _jsx(Switch, { checked: notifyEnabled, onChange: handleToggleNotification, loading: isTogglingNotification })] }) }), _jsx("div", { className: "bg-[#f5f5f5] mx-4 mt-3 rounded-lg overflow-hidden", children: _jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm w-full", onClick: handleToggleBlock, disabled: isTogglingBlock, children: [_jsx(Icon, { icon: "user-del-o", size: 18 }), t(isBlocked ? "unblock_user" : "block_user")] }) }), _jsx("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 mt-3 rounded-lg overflow-hidden", children: _jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmDelete(true), children: [_jsx(Icon, { icon: "trash-o", size: 18 }), t("delete_conversation")] }) })] }), _jsx(Modal, { centered: true, open: confirmDelete, onOk: handleConfirmDelete, onCancel: () => setConfirmDelete(false), title: t("delete_conversation_confirm_title"), okText: t("delete_conversation"), cancelText: t("cancel"), okType: "danger", confirmLoading: isDeleting, getContainer: false, children: _jsx("p", { children: t("delete_conversation_confirm_message") }) })] }));
69
+ };
70
+ export default SingleThreadInfo;
@@ -1,3 +1,9 @@
1
- declare const ThreadInfo: () => import("react/jsx-runtime").JSX.Element | null;
1
+ import { GroupThreadInfoConfig } from "./GroupThreadInfo";
2
+ export interface ThreadInfoProps {
3
+ isOpen?: boolean;
4
+ onClose?: () => void;
5
+ groupConfig?: GroupThreadInfoConfig;
6
+ }
7
+ declare const ThreadInfo: ({ isOpen, onClose, groupConfig }: ThreadInfoProps) => import("react/jsx-runtime").JSX.Element | null;
2
8
  export default ThreadInfo;
3
9
  //# sourceMappingURL=ThreadInfo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfo.tsx"],"names":[],"mappings":"AAOA,QAAA,MAAM,UAAU,sDAsCf,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfo.tsx"],"names":[],"mappings":"AAGA,OAAwB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAO3E,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAED,QAAA,MAAM,UAAU,GAAI,kCAAyC,eAAe,mDAsD3E,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -2,16 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import useConversationStore from "../../store/conversation";
3
3
  import UserSection from "./UserSection";
4
4
  import SessionSection, { SessionDetailCard } from "./SessionSection";
5
+ import GroupThreadInfo from "./GroupThreadInfo";
6
+ import SingleThreadInfo from "./SingleThreadInfo";
5
7
  import { useGetListSessionByConversation } from "../../hooks/session/useGetListSessionByConversation";
6
8
  import { useMemo } from "react";
7
9
  import { PAGE_SIZE } from "../../constants";
8
- const ThreadInfo = () => {
10
+ import { PeerType } from "../../types/conversation";
11
+ const ThreadInfo = ({ isOpen = true, onClose, groupConfig }) => {
9
12
  const conversationData = useConversationStore((state) => state.conversationData);
10
13
  const conversationId = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
14
+ const isGroup = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.GROUP;
15
+ const isBot = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.BOT;
11
16
  const { data: sessions, isLoading } = useGetListSessionByConversation({
12
17
  conversationId,
13
18
  page: 1,
14
19
  pageSize: PAGE_SIZE.large,
20
+ enabled: isBot,
15
21
  });
16
22
  const { closedSessions, activeSession } = useMemo(() => {
17
23
  if (!sessions)
@@ -23,6 +29,12 @@ const ThreadInfo = () => {
23
29
  if (!conversationId) {
24
30
  return null;
25
31
  }
26
- return (_jsxs("div", { className: "h-full w-[330px] bg-white shadow-md flex flex-col relative", children: [_jsxs("div", { className: `flex-grow overflow-y-auto ${activeSession ? "pb-60" : ""}`, children: [_jsx(UserSection, {}), _jsx(SessionSection, { sessions: closedSessions, isLoading: isLoading })] }), activeSession && (_jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 z-20 bg-white backdrop-blur-sm", children: _jsx(SessionDetailCard, { session: activeSession, isActive: true }) }))] }));
32
+ if (isGroup) {
33
+ return isOpen ? _jsx(GroupThreadInfo, { onClose: onClose, config: groupConfig }) : null;
34
+ }
35
+ if (!isOpen && !isBot) {
36
+ return null;
37
+ }
38
+ return (_jsxs("div", { className: "h-full flex flex-row", children: [isOpen && _jsx(SingleThreadInfo, { onClose: onClose }), isBot && (_jsxs("div", { className: "h-full w-[330px] bg-white shadow-md flex flex-col relative", children: [_jsxs("div", { className: `flex-grow overflow-y-auto ${activeSession ? "pb-60" : ""}`, children: [_jsx(UserSection, {}), _jsx(SessionSection, { sessions: closedSessions, isLoading: isLoading })] }), activeSession && (_jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 z-20 bg-white backdrop-blur-sm", children: _jsx(SessionDetailCard, { session: activeSession, isActive: true }) }))] }))] }));
27
39
  };
28
40
  export default ThreadInfo;
@@ -7,4 +7,5 @@ export declare const PAGE_SIZE: {
7
7
  };
8
8
  export declare const MSG_ITEM_PREFIX = "msg-item-";
9
9
  export declare const MSG_ITEM_CONTENT_PREFIX = "msg-item-content-";
10
+ export declare const PERMISSION_SWITCH_COLOR = "#1B3FE4";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,qFAC2D,CAAC;AAErF,eAAO,MAAM,SAAS;;;;;CAKZ,CAAC;AAEX,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,uBAAuB,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,qFAC2D,CAAC;AAErF,eAAO,MAAM,SAAS;;;;;CAKZ,CAAC;AAEX,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAI3D,eAAO,MAAM,uBAAuB,YAAY,CAAC"}
@@ -7,3 +7,6 @@ export const PAGE_SIZE = {
7
7
  };
8
8
  export const MSG_ITEM_PREFIX = "msg-item-";
9
9
  export const MSG_ITEM_CONTENT_PREFIX = "msg-item-content-";
10
+ // antd Switch defaults to antd's own purple — override locally with the
11
+ // theme's primary blue (primary-400) to match the permission-config mockups.
12
+ export const PERMISSION_SWITCH_COLOR = "#1B3FE4";
@@ -1 +1 @@
1
- {"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAuCA,eAAO,MAAM,cAAc,YAiW1B,CAAC"}
1
+ {"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAuCA,eAAO,MAAM,cAAc,YAqY1B,CAAC"}
@@ -201,6 +201,21 @@ export const useGlobalEvent = () => {
201
201
  void refreshCurrentGroupState();
202
202
  emitter.emit("GROUP_MEMBERS_CHANGED");
203
203
  };
204
+ const groupInfoChangedHandler = () => {
205
+ void refreshCurrentGroupState();
206
+ };
207
+ const groupDismissedHandler = ({ data }) => {
208
+ const currentConversation = useConversationStore.getState().currentConversation;
209
+ if ((currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.groupID) === data.groupID) {
210
+ useConversationStore.getState().clearSelectedConversation();
211
+ }
212
+ useConversationStore.setState((state) => ({
213
+ conversationList: state.conversationList.filter((conversation) => conversation.groupID !== data.groupID),
214
+ }));
215
+ };
216
+ const blackListChangedHandler = () => {
217
+ emitter.emit("BLACK_LIST_CHANGED");
218
+ };
204
219
  // createGroup()'s own response resolving is not enough to guarantee the
205
220
  // new group shows up in conversationList — OnConversationChanged/
206
221
  // OnNewConversation for it can arrive after an unspecified delay (per
@@ -234,6 +249,12 @@ export const useGlobalEvent = () => {
234
249
  DChatSDK.on(CbEvents.OnGroupMemberAdded, groupMemberChangedHandler);
235
250
  DChatSDK.on(CbEvents.OnGroupMemberDeleted, groupMemberChangedHandler);
236
251
  DChatSDK.on(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
252
+ // group info
253
+ DChatSDK.on(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
254
+ DChatSDK.on(CbEvents.OnGroupDismissed, groupDismissedHandler);
255
+ // black list
256
+ DChatSDK.on(CbEvents.OnBlackAdded, blackListChangedHandler);
257
+ DChatSDK.on(CbEvents.OnBlackDeleted, blackListChangedHandler);
237
258
  //busines notification
238
259
  DChatSDK.on(CbEvents.OnRecvCustomBusinessMessage, businessNotificationHandler);
239
260
  };
@@ -260,6 +281,12 @@ export const useGlobalEvent = () => {
260
281
  DChatSDK.off(CbEvents.OnGroupMemberAdded, groupMemberChangedHandler);
261
282
  DChatSDK.off(CbEvents.OnGroupMemberDeleted, groupMemberChangedHandler);
262
283
  DChatSDK.off(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
284
+ // group info
285
+ DChatSDK.off(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
286
+ DChatSDK.off(CbEvents.OnGroupDismissed, groupDismissedHandler);
287
+ // black list
288
+ DChatSDK.off(CbEvents.OnBlackAdded, blackListChangedHandler);
289
+ DChatSDK.off(CbEvents.OnBlackDeleted, blackListChangedHandler);
263
290
  //busines notification
264
291
  DChatSDK.off(CbEvents.OnRecvCustomBusinessMessage, businessNotificationHandler);
265
292
  };
@@ -0,0 +1,7 @@
1
+ interface AddGroupMemberParams {
2
+ groupID: string;
3
+ userIDList: string[];
4
+ }
5
+ export declare const useAddGroupMember: () => import("@tanstack/react-query").UseMutationResult<unknown, Error, AddGroupMemberParams, unknown>;
6
+ export {};
7
+ //# sourceMappingURL=useAddGroupMember.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAddGroupMember.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useAddGroupMember.ts"],"names":[],"mappings":"AAKA,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,iBAAiB,wGAgB7B,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { useMutation } from "@tanstack/react-query";
3
+ import { DChatSDK } from "../../constants/sdk";
4
+ export const useAddGroupMember = () => {
5
+ return useMutation({
6
+ mutationFn: async ({ groupID, userIDList }) => {
7
+ console.log("[AddGroupMember] inviteUserToGroup", { groupID, userIDList });
8
+ const { data } = await DChatSDK.inviteUserToGroup({
9
+ groupID,
10
+ userIDList,
11
+ reason: "",
12
+ });
13
+ console.log("[AddGroupMember] inviteUserToGroup success", { groupID, userIDList, data });
14
+ return data;
15
+ },
16
+ onError: (error, { groupID, userIDList }) => {
17
+ console.error("[AddGroupMember] inviteUserToGroup failed", { groupID, userIDList, error });
18
+ },
19
+ });
20
+ };
@@ -0,0 +1,4 @@
1
+ export declare const useDismissGroup: () => import("@tanstack/react-query").UseMutationResult<unknown, Error, {
2
+ groupID: string;
3
+ }, unknown>;
4
+ //# sourceMappingURL=useDismissGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDismissGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useDismissGroup.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,eAAe;aAEmB,MAAM;WAYpD,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { useMutation } from "@tanstack/react-query";
3
+ import { DChatSDK } from "../../constants/sdk";
4
+ import useConversationStore from "../../store/conversation";
5
+ import useSessionStore from "../../store/session";
6
+ import { SessionStatus } from "../../types/chat";
7
+ import emitter from "../../utils/events";
8
+ export const useDismissGroup = () => {
9
+ return useMutation({
10
+ mutationFn: async ({ groupID }) => {
11
+ const { data } = await DChatSDK.dismissGroup(groupID);
12
+ emitter.emit("GROUP_MEMBERS_CHANGED");
13
+ return data;
14
+ },
15
+ onSuccess: () => {
16
+ useConversationStore.getState().clearSelectedConversation();
17
+ useSessionStore
18
+ .getState()
19
+ .setFilterSummary({ status: SessionStatus.IN_PROCESS });
20
+ },
21
+ });
22
+ };
@@ -6,5 +6,8 @@ export interface EligibleAccountItem {
6
6
  export declare const useEligibleAccounts: (search: string, enabled?: boolean) => {
7
7
  accounts: EligibleAccountItem[];
8
8
  isLoading: boolean;
9
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<import("../../types/dto").BaseResponse<import("../../types/dto").IGroupCandidateResponse[]>, unknown>, Error>>;
10
+ hasNextPage: boolean;
11
+ isFetchingNextPage: boolean;
9
12
  };
10
13
  //# sourceMappingURL=useEligibleAccounts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useEligibleAccounts.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useEligibleAccounts.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAKD,eAAO,MAAM,mBAAmB,GAAI,QAAQ,MAAM,EAAE,iBAAc;;;CAoBjE,CAAC"}
1
+ {"version":3,"file":"useEligibleAccounts.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useEligibleAccounts.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAKD,eAAO,MAAM,mBAAmB,GAAI,QAAQ,MAAM,EAAE,iBAAc;;;;;;CAwBjE,CAAC"}
@@ -4,20 +4,20 @@ import { useGetGroupCandidates } from "./useGetGroupCandidates";
4
4
  // only eligible (active) accounts and supports searchTerm filtering, so no
5
5
  // client-side active/inactive filtering is needed here (Q3 resolved).
6
6
  export const useEligibleAccounts = (search, enabled = true) => {
7
- const { data, isLoading } = useGetGroupCandidates({
7
+ const { candidates, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useGetGroupCandidates({
8
8
  searchTerm: search.trim(),
9
9
  enabled,
10
10
  });
11
- const accounts = useMemo(() => {
12
- var _a;
13
- return ((_a = data === null || data === void 0 ? void 0 : data.data) !== null && _a !== void 0 ? _a : []).map((candidate) => ({
14
- userID: candidate.id,
15
- displayName: candidate.fullName,
16
- avatar: candidate.avatar,
17
- }));
18
- }, [data]);
11
+ const accounts = useMemo(() => candidates.map((candidate) => ({
12
+ userID: candidate.id,
13
+ displayName: candidate.fullName,
14
+ avatar: candidate.avatar,
15
+ })), [candidates]);
19
16
  return {
20
17
  accounts,
21
18
  isLoading,
19
+ fetchNextPage,
20
+ hasNextPage,
21
+ isFetchingNextPage,
22
22
  };
23
23
  };
@@ -0,0 +1,9 @@
1
+ import type { EligibleAccountItem } from "./useEligibleAccounts";
2
+ export declare const useEligibleAccountsForGroup: (search: string, existingMemberIDs: string[], enabled?: boolean) => {
3
+ accounts: EligibleAccountItem[];
4
+ isLoading: boolean;
5
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<import("../../types/dto").BaseResponse<import("../../types/dto").IGroupCandidateResponse[]>, unknown>, Error>>;
6
+ hasNextPage: boolean;
7
+ isFetchingNextPage: boolean;
8
+ };
9
+ //# sourceMappingURL=useEligibleAccountsForGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEligibleAccountsForGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useEligibleAccountsForGroup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAKjE,eAAO,MAAM,2BAA2B,GACtC,QAAQ,MAAM,EACd,mBAAmB,MAAM,EAAE,EAC3B,iBAAc;;;;;;CAgCf,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { useMemo } from "react";
2
+ import { useGetGroupCandidates } from "./useGetGroupCandidates";
3
+ // getGroupCandidates has no server-side "exclude group members" filter yet
4
+ // (BE gap tracked in DROPPII-30003 breakdown, Q1) — filtering existing group
5
+ // members out client-side here as an interim until BE adds it.
6
+ export const useEligibleAccountsForGroup = (search, existingMemberIDs, enabled = true) => {
7
+ const { candidates, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useGetGroupCandidates({
8
+ searchTerm: search.trim(),
9
+ enabled,
10
+ });
11
+ const existingMemberIDSet = useMemo(() => new Set(existingMemberIDs), [existingMemberIDs]);
12
+ const accounts = useMemo(() => candidates
13
+ .filter((candidate) => !existingMemberIDSet.has(candidate.id))
14
+ .map((candidate) => ({
15
+ userID: candidate.id,
16
+ displayName: candidate.fullName,
17
+ avatar: candidate.avatar,
18
+ })), [candidates, existingMemberIDSet]);
19
+ return {
20
+ accounts,
21
+ isLoading,
22
+ fetchNextPage,
23
+ hasNextPage,
24
+ isFetchingNextPage,
25
+ };
26
+ };