@droppii-org/chat-sdk 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/common/ConfirmModal.d.ts +18 -0
- package/dist/components/common/ConfirmModal.d.ts.map +1 -0
- package/dist/components/common/ConfirmModal.js +15 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +20 -14
- package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
- package/dist/components/group/AdminPermissionModal.js +37 -13
- package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
- package/dist/components/message/GroupMembersDrawer.js +27 -14
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +17 -3
- package/dist/components/message/item/MessageBubble.d.ts +3 -2
- package/dist/components/message/item/MessageBubble.d.ts.map +1 -1
- package/dist/components/message/item/MessageBubble.js +8 -2
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +22 -7
- package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/GroupThreadInfo.js +43 -14
- package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/SingleThreadInfo.js +3 -2
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +32 -3
- package/dist/hooks/group/useGroupPermission.js +1 -1
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
- package/dist/hooks/session/useConversationSessionState.js +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +5 -0
- package/dist/locales/vi/common.json +14 -8
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +16 -4
- package/dist/styles/global.css +1 -1
- package/dist/utils/common.d.ts +1 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +18 -0
- package/dist/utils/fileValidation.d.ts.map +1 -1
- package/dist/utils/fileValidation.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Input, Modal, Switch, message } from "antd";
|
|
4
|
+
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
4
5
|
import { useTranslation } from "react-i18next";
|
|
5
6
|
import { GroupMemberRole, GroupStatus, MessageReceiveOptType } from "@droppii-org/wasm-client-sdk";
|
|
6
7
|
import { Icon } from "../../components/icon";
|
|
@@ -13,12 +14,12 @@ import { useToggleGroupSendMessage } from "../../hooks/group/useToggleGroupSendM
|
|
|
13
14
|
import { useDismissGroup } from "../../hooks/group/useDismissGroup";
|
|
14
15
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
15
16
|
import { useUploadFile } from "../../hooks/common/useUploadFile";
|
|
16
|
-
import {
|
|
17
|
+
import { ACCEPTED_IMAGE_TYPES, MAX_IMAGE_SIZE_MB } from "../../utils/fileValidation";
|
|
17
18
|
import AdminPermissionModal from "../../components/group/AdminPermissionModal";
|
|
18
19
|
import MemberPermissionPanel from "../../components/group/MemberPermissionPanel";
|
|
19
20
|
import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
|
|
20
21
|
const GroupThreadInfo = ({ onClose, config }) => {
|
|
21
|
-
var _a, _b, _c;
|
|
22
|
+
var _a, _b, _c, _d;
|
|
22
23
|
const { t } = useTranslation();
|
|
23
24
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
24
25
|
const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
|
|
@@ -31,6 +32,10 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
31
32
|
// per-permission.
|
|
32
33
|
const isAdminOrOwner = (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Admin ||
|
|
33
34
|
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Owner;
|
|
35
|
+
// "Cho phép gửi tin nhắn" (group-wide mute) is owner-only per BR-001 —
|
|
36
|
+
// unlike other admin-manageable rows in this panel, admins may view but
|
|
37
|
+
// not change this toggle.
|
|
38
|
+
const isOwner = (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Owner;
|
|
34
39
|
const [editingField, setEditingField] = useState(null);
|
|
35
40
|
const [nameValue, setNameValue] = useState((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _a !== void 0 ? _a : "");
|
|
36
41
|
const [descriptionValue, setDescriptionValue] = useState((_b = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _b !== void 0 ? _b : "");
|
|
@@ -48,7 +53,7 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
48
53
|
useEffect(() => {
|
|
49
54
|
setAllowSendMessage((currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status) !== GroupStatus.Muted);
|
|
50
55
|
}, [currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status]);
|
|
51
|
-
const { mutate: updateGroupInfo } = useUpdateGroupInfo();
|
|
56
|
+
const { mutate: updateGroupInfo, isPending: isSavingGroupInfo } = useUpdateGroupInfo();
|
|
52
57
|
const { mutate: uploadFile, isPending: isAvatarUploading } = useUploadFile();
|
|
53
58
|
const { mutate: toggleSendMessage, isPending: isTogglingSendMessage } = useToggleGroupSendMessage();
|
|
54
59
|
const { mutate: toggleNotification, isPending: isTogglingNotification } = useToggleConversationNotification();
|
|
@@ -65,12 +70,23 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
65
70
|
setDescriptionValue((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _a !== void 0 ? _a : "");
|
|
66
71
|
setEditingField("description");
|
|
67
72
|
};
|
|
73
|
+
const cancelEditName = () => {
|
|
74
|
+
var _a;
|
|
75
|
+
setNameValue((_a = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _a !== void 0 ? _a : "");
|
|
76
|
+
setEditingField(null);
|
|
77
|
+
};
|
|
68
78
|
const saveName = () => {
|
|
79
|
+
if (isSavingGroupInfo)
|
|
80
|
+
return;
|
|
69
81
|
const trimmed = nameValue.trim();
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
if (!groupID || !trimmed || trimmed === (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName)) {
|
|
83
|
+
setEditingField(null);
|
|
72
84
|
return;
|
|
73
|
-
|
|
85
|
+
}
|
|
86
|
+
updateGroupInfo({ groupID, groupName: trimmed }, {
|
|
87
|
+
onSuccess: () => setEditingField(null),
|
|
88
|
+
onError: () => message.error(t("update_group_info_failed")),
|
|
89
|
+
});
|
|
74
90
|
};
|
|
75
91
|
const cancelEditDescription = () => {
|
|
76
92
|
var _a;
|
|
@@ -78,11 +94,17 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
78
94
|
setEditingField(null);
|
|
79
95
|
};
|
|
80
96
|
const saveDescription = () => {
|
|
97
|
+
if (isSavingGroupInfo)
|
|
98
|
+
return;
|
|
81
99
|
const trimmed = descriptionValue.trim();
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
if (!groupID || trimmed === (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction)) {
|
|
101
|
+
setEditingField(null);
|
|
84
102
|
return;
|
|
85
|
-
|
|
103
|
+
}
|
|
104
|
+
updateGroupInfo({ groupID, introduction: trimmed }, {
|
|
105
|
+
onSuccess: () => setEditingField(null),
|
|
106
|
+
onError: () => message.error(t("update_group_info_failed")),
|
|
107
|
+
});
|
|
86
108
|
};
|
|
87
109
|
const handleAvatarClick = () => {
|
|
88
110
|
var _a;
|
|
@@ -96,9 +118,13 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
96
118
|
event.target.value = "";
|
|
97
119
|
if (!file || !groupID)
|
|
98
120
|
return;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
121
|
+
if (!ACCEPTED_IMAGE_TYPES.includes(file.type)) {
|
|
122
|
+
message.error(t("avatar_invalid_format"));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const fileSizeMB = file.size / 1024 / 1024;
|
|
126
|
+
if (fileSizeMB > MAX_IMAGE_SIZE_MB) {
|
|
127
|
+
message.error(t("avatar_size_exceeded", { maxSize: MAX_IMAGE_SIZE_MB }));
|
|
102
128
|
return;
|
|
103
129
|
}
|
|
104
130
|
uploadFile(file, {
|
|
@@ -125,7 +151,7 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
125
151
|
toggleNotification({ conversationID, notify: checked });
|
|
126
152
|
};
|
|
127
153
|
const handleToggleSendMessage = (checked) => {
|
|
128
|
-
if (!groupID)
|
|
154
|
+
if (!groupID || !isOwner)
|
|
129
155
|
return;
|
|
130
156
|
const previous = allowSendMessage;
|
|
131
157
|
setAllowSendMessage(checked);
|
|
@@ -187,6 +213,9 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
187
213
|
if (!groupID) {
|
|
188
214
|
return null;
|
|
189
215
|
}
|
|
190
|
-
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(GroupCompositeAvatar, { groupID: groupID, faceURL: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.faceURL, ownerUserID: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.ownerUserID, groupName: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName, size: 56 }), isAvatarUploading && (_jsx("span", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-black/30", children: _jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" }) })), permissions.updateGroupAvatar && (_jsxs(_Fragment, { children: [_jsx("input", { ref: avatarInputRef, type: "file", accept: "image/jpeg, image/png, image/jpg", className: "hidden", onChange: handleAvatarSelect }), _jsx("button", { type: "button", disabled: isAvatarUploading, onClick: handleAvatarClick, 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() === ((_c = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _c !== void 0 ? _c : ""), 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, forceRender: true, 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, forceRender: true, 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, forceRender: true, 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, forceRender: true, children: _jsx("p", { className: "text-sm text-gray-500", children: t("feature_coming_soon") }) }))] }));
|
|
216
|
+
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(GroupCompositeAvatar, { groupID: groupID, faceURL: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.faceURL, ownerUserID: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.ownerUserID, groupName: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName, size: 56 }), isAvatarUploading && (_jsx("span", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-black/30", children: _jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" }) })), permissions.updateGroupAvatar && (_jsxs(_Fragment, { children: [_jsx("input", { ref: avatarInputRef, type: "file", accept: ACCEPTED_IMAGE_TYPES.join(", "), className: "hidden", onChange: handleAvatarSelect }), _jsx("button", { type: "button", disabled: isAvatarUploading, onClick: handleAvatarClick, 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" && (_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 }))] })) })] }), editingField === "name" && (_jsx("div", { className: "px-4 pb-4", children: _jsxs("div", { className: "flex flex-col gap-3 bg-[#f5f5f5] rounded-lg p-3", children: [_jsx(Input, { autoFocus: true, value: nameValue, onChange: (event) => setNameValue(event.target.value), maxLength: 255, showCount: true, disabled: isSavingGroupInfo, 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-lg border border-primary-500 text-primary-500 text-sm font-medium disabled:border-gray-200 disabled:text-gray-300", disabled: isSavingGroupInfo, onClick: cancelEditName, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg bg-primary-500 text-white text-sm font-medium disabled:bg-gray-100 disabled:text-gray-300", disabled: isSavingGroupInfo ||
|
|
217
|
+
!nameValue.trim() ||
|
|
218
|
+
nameValue.trim() === ((_c = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _c !== void 0 ? _c : ""), onClick: saveName, children: t("save") })] })] }) })), _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, showCount: true, disabled: isSavingGroupInfo, 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-lg border border-primary-500 text-primary-500 text-sm font-medium disabled:border-gray-200 disabled:text-gray-300", disabled: isSavingGroupInfo, onClick: cancelEditDescription, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg bg-primary-500 text-white text-sm font-medium disabled:bg-gray-100 disabled:text-gray-300", disabled: isSavingGroupInfo ||
|
|
219
|
+
descriptionValue.trim() === ((_d = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _d !== void 0 ? _d : ""), 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) && isOwner && (_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" })] })), isAdminOrOwner && (_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: [isAdminOrOwner && (_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(ConfirmModal, { 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, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(ConfirmModal, { 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, children: _jsx("p", { children: t("disband_group_confirm_message") }) }), _jsx(ConfirmModal, { 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, 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, forceRender: true, children: _jsx("p", { className: "text-sm text-gray-500", children: t("feature_coming_soon") }) }))] }));
|
|
191
220
|
};
|
|
192
221
|
export default GroupThreadInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SingleThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SingleThreadInfo.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SingleThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SingleThreadInfo.tsx"],"names":[],"mappings":"AAaA,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,gBAAgB,GAAI,aAAa,qBAAqB,mDAiJ3D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import { Avatar,
|
|
3
|
+
import { Avatar, Switch, message } from "antd";
|
|
4
|
+
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
4
5
|
import { useTranslation } from "react-i18next";
|
|
5
6
|
import { MessageReceiveOptType } from "@droppii-org/wasm-client-sdk";
|
|
6
7
|
import { Icon } from "../../components/icon";
|
|
@@ -65,6 +66,6 @@ const SingleThreadInfo = ({ onClose }) => {
|
|
|
65
66
|
if (!conversationID) {
|
|
66
67
|
return null;
|
|
67
68
|
}
|
|
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(
|
|
69
|
+
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(ConfirmModal, { open: confirmDelete, onOk: handleConfirmDelete, onCancel: () => setConfirmDelete(false), title: t("delete_conversation_confirm_title"), okText: t("delete_conversation"), cancelText: t("cancel"), okType: "danger", confirmLoading: isDeleting, children: _jsx("p", { children: t("delete_conversation_confirm_message") }) })] }));
|
|
69
70
|
};
|
|
70
71
|
export default SingleThreadInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,cAAc,YA8a1B,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
2
3
|
import { useLatest } from "ahooks";
|
|
3
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
4
5
|
import { CbEvents, MessageType, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
@@ -22,11 +23,16 @@ export const useGlobalEvent = () => {
|
|
|
22
23
|
const { user } = useChatContext();
|
|
23
24
|
const userRef = useLatest(user);
|
|
24
25
|
const queryClient = useQueryClient();
|
|
26
|
+
const router = useRouter();
|
|
27
|
+
const pathname = usePathname();
|
|
28
|
+
const searchParams = useSearchParams();
|
|
29
|
+
const searchParamsRef = useLatest(searchParams);
|
|
25
30
|
const { updateConnectStatus, updateSyncStatus, getSelfUserInfo } = useChatContext();
|
|
26
31
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
27
32
|
const getConversationListByReq = useConversationStore((state) => state.getConversationListByReq);
|
|
28
33
|
const getCurrentGroupInfoByReq = useConversationStore((state) => state.getCurrentGroupInfoByReq);
|
|
29
34
|
const getSelfGroupPermissionByReq = useConversationStore((state) => state.getSelfGroupPermissionByReq);
|
|
35
|
+
const getCurrentMemberInGroupByReq = useConversationStore((state) => state.getCurrentMemberInGroupByReq);
|
|
30
36
|
const { mutate: refetchChatToken } = useRefetchChatToken();
|
|
31
37
|
const accessToken = useAuthStore((state) => state.accessToken);
|
|
32
38
|
const chatToken = useAuthStore((state) => state.chatToken);
|
|
@@ -43,6 +49,9 @@ export const useGlobalEvent = () => {
|
|
|
43
49
|
await Promise.all([
|
|
44
50
|
getCurrentGroupInfoByReq(currentConversation.groupID),
|
|
45
51
|
getSelfGroupPermissionByReq(currentConversation.groupID),
|
|
52
|
+
// roleLevel comes only from this call — without it, isAdminOrOwner
|
|
53
|
+
// checks (disband, mute toggle, etc.) fail closed.
|
|
54
|
+
getCurrentMemberInGroupByReq(currentConversation.groupID),
|
|
46
55
|
]);
|
|
47
56
|
};
|
|
48
57
|
const revokedMessageHandler = ({ data }) => {
|
|
@@ -210,10 +219,28 @@ export const useGlobalEvent = () => {
|
|
|
210
219
|
const groupMemberInfoChangedHandler = () => {
|
|
211
220
|
void refreshCurrentGroupState();
|
|
212
221
|
};
|
|
213
|
-
|
|
222
|
+
// Shared by OnGroupDismissed (group disbanded for everyone) and
|
|
223
|
+
// OnJoinedGroupDeleted (this client specifically removed from the group,
|
|
224
|
+
// e.g. kicked by an admin) — both leave the current user out of the group,
|
|
225
|
+
// so the local conversation state needs the same cleanup either way.
|
|
226
|
+
//
|
|
227
|
+
// Clearing the Zustand store alone isn't enough: both consumer screens
|
|
228
|
+
// (DeskConversationList for CRM, useActiveConversation for BIZ) re-derive
|
|
229
|
+
// the open conversation from the ?threadId= URL param on their own
|
|
230
|
+
// effects, so they'd immediately re-select the (now filtered-out) group
|
|
231
|
+
// unless the URL itself is cleared too — hence the router.replace below.
|
|
232
|
+
const leftGroupHandler = ({ data }) => {
|
|
233
|
+
var _a;
|
|
214
234
|
const currentConversation = useConversationStore.getState().currentConversation;
|
|
215
235
|
if ((currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.groupID) === data.groupID) {
|
|
216
236
|
useConversationStore.getState().clearSelectedConversation();
|
|
237
|
+
const newSearchParams = new URLSearchParams((_a = searchParamsRef.current) === null || _a === void 0 ? void 0 : _a.toString());
|
|
238
|
+
newSearchParams.delete("threadId");
|
|
239
|
+
newSearchParams.delete("userId");
|
|
240
|
+
newSearchParams.delete("groupId");
|
|
241
|
+
newSearchParams.delete("botId");
|
|
242
|
+
const query = newSearchParams.toString();
|
|
243
|
+
router.replace(query ? `${pathname}?${query}` : pathname);
|
|
217
244
|
}
|
|
218
245
|
useConversationStore.setState((state) => ({
|
|
219
246
|
conversationList: state.conversationList.filter((conversation) => conversation.groupID !== data.groupID),
|
|
@@ -258,7 +285,8 @@ export const useGlobalEvent = () => {
|
|
|
258
285
|
// group info
|
|
259
286
|
DChatSDK.on(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
|
|
260
287
|
DChatSDK.on(CbEvents.OnGroupMemberInfoChanged, groupMemberInfoChangedHandler);
|
|
261
|
-
DChatSDK.on(CbEvents.OnGroupDismissed,
|
|
288
|
+
DChatSDK.on(CbEvents.OnGroupDismissed, leftGroupHandler);
|
|
289
|
+
DChatSDK.on(CbEvents.OnJoinedGroupDeleted, leftGroupHandler);
|
|
262
290
|
// black list
|
|
263
291
|
DChatSDK.on(CbEvents.OnBlackAdded, blackListChangedHandler);
|
|
264
292
|
DChatSDK.on(CbEvents.OnBlackDeleted, blackListChangedHandler);
|
|
@@ -291,7 +319,8 @@ export const useGlobalEvent = () => {
|
|
|
291
319
|
// group info
|
|
292
320
|
DChatSDK.off(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
|
|
293
321
|
DChatSDK.off(CbEvents.OnGroupMemberInfoChanged, groupMemberInfoChangedHandler);
|
|
294
|
-
DChatSDK.off(CbEvents.OnGroupDismissed,
|
|
322
|
+
DChatSDK.off(CbEvents.OnGroupDismissed, leftGroupHandler);
|
|
323
|
+
DChatSDK.off(CbEvents.OnJoinedGroupDeleted, leftGroupHandler);
|
|
295
324
|
// black list
|
|
296
325
|
DChatSDK.off(CbEvents.OnBlackAdded, blackListChangedHandler);
|
|
297
326
|
DChatSDK.off(CbEvents.OnBlackDeleted, blackListChangedHandler);
|
|
@@ -48,7 +48,7 @@ const ROLE_DEFAULT_PERMISSIONS = {
|
|
|
48
48
|
};
|
|
49
49
|
export const permissionListToGroupPermissions = (permissionList, roleLevel) => {
|
|
50
50
|
var _a;
|
|
51
|
-
if (!permissionList) {
|
|
51
|
+
if (!permissionList || permissionList.length === 0) {
|
|
52
52
|
return ((_a = ROLE_DEFAULT_PERMISSIONS[roleLevel]) !== null && _a !== void 0 ? _a : ROLE_DEFAULT_PERMISSIONS[GroupMemberRole.Normal]);
|
|
53
53
|
}
|
|
54
54
|
return Object.fromEntries(Object.entries(GROUP_PERMISSION_KEY).map(([field, permissionString]) => [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationSessionState.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useConversationSessionState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,iCAAiC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GAAI,gDAIzC,iCAAiC;;;;;
|
|
1
|
+
{"version":3,"file":"useConversationSessionState.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useConversationSessionState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,iCAAiC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GAAI,gDAIzC,iCAAiC;;;;;CAoBnC,CAAC"}
|
|
@@ -7,6 +7,7 @@ export const useConversationSessionState = ({ conversationId, conversationType,
|
|
|
7
7
|
const currentGroupId = isGroupConversation ? groupId : undefined;
|
|
8
8
|
const { data: conversationSessions, refetch: refetchConversationSessions, } = useGetListSessionByConversation({
|
|
9
9
|
conversationId,
|
|
10
|
+
enabled: !isGroupConversation,
|
|
10
11
|
});
|
|
11
12
|
const latestConversationSession = conversationSessions === null || conversationSessions === void 0 ? void 0 : conversationSessions[0];
|
|
12
13
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layout/index.tsx"],"names":[],"mappings":"AAIA,OAAO,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layout/index.tsx"],"names":[],"mappings":"AAIA,OAAO,iBAAiB,CAAC;AAUzB,UAAU,eAAe;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AACD,QAAA,MAAM,UAAU,GAAI,cAAc,eAAe,4CAKhD,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
package/dist/layout/index.js
CHANGED
|
@@ -3,7 +3,12 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useGlobalEvent } from "../hooks/global/useGlobalEvent";
|
|
4
4
|
import dayjs from "dayjs";
|
|
5
5
|
import "dayjs/locale/vi";
|
|
6
|
+
import utc from "dayjs/plugin/utc";
|
|
7
|
+
import timezone from "dayjs/plugin/timezone";
|
|
6
8
|
import { useSyncUsersInfo } from "../hooks/user/useUsersInfo";
|
|
9
|
+
dayjs.extend(utc);
|
|
10
|
+
dayjs.extend(timezone);
|
|
11
|
+
dayjs.tz.setDefault("Asia/Ho_Chi_Minh");
|
|
7
12
|
dayjs.locale("vi");
|
|
8
13
|
const MainLayout = ({ children }) => {
|
|
9
14
|
useGlobalEvent();
|
|
@@ -102,6 +102,8 @@
|
|
|
102
102
|
"select_status": "Chọn trạng thái",
|
|
103
103
|
"invalid_file_format": "{{fileName}} không đúng định dạng JPG, JPEG, PNG hoặc VIDEO",
|
|
104
104
|
"file_size_exceeded": "{{fileName}} có kích thước tập tin vượt quá {{maxSize}}MB",
|
|
105
|
+
"avatar_invalid_format": "Vui lòng dùng file PNG, JPG, JPEG, WEBP",
|
|
106
|
+
"avatar_size_exceeded": "Vui lòng dùng ảnh dưới {{maxSize}}MB",
|
|
105
107
|
"file_processing_error": "Đã xảy ra lỗi khi xử lý tệp",
|
|
106
108
|
"invalid_document_format": "{{fileName}} không đúng định dạng (chỉ hỗ trợ PDF, DOC, DOCX)",
|
|
107
109
|
"view_message": "Xem tin nhắn",
|
|
@@ -154,19 +156,22 @@
|
|
|
154
156
|
"mute_member_success": "Đã tắt quyền gửi tin nhắn",
|
|
155
157
|
"unmute_member_success": "Đã bật lại quyền gửi tin nhắn",
|
|
156
158
|
"send_message": "Gửi tin nhắn",
|
|
157
|
-
"transfer_owner": "Chuyển quyền
|
|
158
|
-
"transfer_owner_success": "Đã chuyển quyền
|
|
159
|
-
"transfer_owner_confirm_title": "Chuyển quyền
|
|
160
|
-
"transfer_owner_confirm_message": "Bạn có chắc muốn chuyển quyền
|
|
159
|
+
"transfer_owner": "Chuyển quyền sở hữu nhóm",
|
|
160
|
+
"transfer_owner_success": "Đã chuyển quyền sở hữu nhóm",
|
|
161
|
+
"transfer_owner_confirm_title": "Chuyển quyền sở hữu nhóm",
|
|
162
|
+
"transfer_owner_confirm_message": "Bạn có chắc muốn chuyển quyền sở hữu nhóm cho {{name}}?",
|
|
161
163
|
"assign_as_admin": "Chỉ định làm quản trị viên",
|
|
162
164
|
"assign_as_admin_success": "Đã chỉ định làm quản trị viên",
|
|
165
|
+
"assign_admin_confirm_title": "Cấp quyền quản trị viên",
|
|
166
|
+
"assign_admin_confirm_message": "Bạn chắc chắn muốn cấp quyền quản trị viên cho \"{{name}}\" ?",
|
|
167
|
+
"agree": "Đồng ý",
|
|
163
168
|
"remove_from_admin": "Xoá khỏi quản trị viên",
|
|
164
169
|
"remove_from_admin_success": "Đã xoá khỏi quản trị viên",
|
|
165
170
|
"remove_from_group": "Xoá khỏi nhóm",
|
|
166
171
|
"remove_member_success": "Đã xoá thành viên khỏi nhóm",
|
|
167
|
-
"remove_member_failed": "
|
|
168
|
-
"remove_member_confirm_title": "
|
|
169
|
-
"remove_member_confirm_message": "Bạn
|
|
172
|
+
"remove_member_failed": "Xoá thành viên không thành công, vui lòng thử lại!",
|
|
173
|
+
"remove_member_confirm_title": "Xóa khỏi nhóm",
|
|
174
|
+
"remove_member_confirm_message": "Bạn chắc chắn muốn xóa \"{{name}}\" khỏi nhóm ?",
|
|
170
175
|
"remove_and_block": "Xoá và chặn",
|
|
171
176
|
"choose_emoji": "Chọn biểu tượng cảm xúc",
|
|
172
177
|
"emoji_smileys": "Mặt cười",
|
|
@@ -191,6 +196,7 @@
|
|
|
191
196
|
"team_messages": "Tin nhắn nhóm",
|
|
192
197
|
"join_group_required": "Bạn phải là thành viên để gửi tin nhắn",
|
|
193
198
|
"send_message_disabled": "Chỉ quản trị viên được phép gửi tin nhắn",
|
|
199
|
+
"send_message_disabled_group_mute": "Chỉ chủ nhóm và quản trị viên được phép gửi tin nhắn",
|
|
194
200
|
"join_group": "Tham gia",
|
|
195
201
|
"join_group_success": "Tham gia nhóm thành công",
|
|
196
202
|
"join_group_failed": "Tham gia nhóm thất bại",
|
|
@@ -245,4 +251,4 @@
|
|
|
245
251
|
"block_user_success": "Đã chặn người dùng",
|
|
246
252
|
"unblock_user_success": "Đã bỏ chặn người dùng",
|
|
247
253
|
"block_user_failed": "Thao tác chặn/bỏ chặn thất bại"
|
|
248
|
-
}
|
|
254
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAM3C,QAAA,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAM3C,QAAA,MAAM,oBAAoB,gFA4SvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
|
|
@@ -102,6 +102,9 @@ const useConversationStore = create((set, get) => ({
|
|
|
102
102
|
await Promise.all([
|
|
103
103
|
get().getCurrentGroupInfoByReq(conversation.groupID),
|
|
104
104
|
get().getSelfGroupPermissionByReq(conversation.groupID),
|
|
105
|
+
// roleLevel comes only from this call — without it, role checks
|
|
106
|
+
// fail closed until an unrelated WS event happens to refresh it.
|
|
107
|
+
get().getCurrentMemberInGroupByReq(conversation.groupID),
|
|
105
108
|
]);
|
|
106
109
|
}
|
|
107
110
|
else {
|
|
@@ -152,13 +155,14 @@ const useConversationStore = create((set, get) => ({
|
|
|
152
155
|
memberInfo = data[0];
|
|
153
156
|
}
|
|
154
157
|
catch (error) {
|
|
155
|
-
set(() => ({ currentMemberInGroup: undefined }));
|
|
156
158
|
console.error("Error fetching group members:", error);
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
159
161
|
const currentConversationData = get().conversationData;
|
|
160
162
|
const shouldApplyMemberInfo = (currentConversationData === null || currentConversationData === void 0 ? void 0 : currentConversationData.groupID) === groupID &&
|
|
161
163
|
isGroupSession(currentConversationData === null || currentConversationData === void 0 ? void 0 : currentConversationData.conversationType);
|
|
164
|
+
// Runs concurrently with getSelfGroupPermissionByReq — only apply to
|
|
165
|
+
// the conversation still open, so a stale response can't clobber it.
|
|
162
166
|
if (!shouldApplyMemberInfo) {
|
|
163
167
|
return;
|
|
164
168
|
}
|
|
@@ -186,9 +190,17 @@ const useConversationStore = create((set, get) => ({
|
|
|
186
190
|
if (!shouldApplyMemberInfo || !selfInfo) {
|
|
187
191
|
return;
|
|
188
192
|
}
|
|
189
|
-
set((state) =>
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
set((state) => {
|
|
194
|
+
var _a;
|
|
195
|
+
// Only merge onto a record for the SAME group, or a race can spread
|
|
196
|
+
// a stale conversation's roleLevel onto the new groupID.
|
|
197
|
+
const base = ((_a = state.currentMemberInGroup) === null || _a === void 0 ? void 0 : _a.groupID) === groupID
|
|
198
|
+
? state.currentMemberInGroup
|
|
199
|
+
: undefined;
|
|
200
|
+
return {
|
|
201
|
+
currentMemberInGroup: Object.assign(Object.assign({}, base), { groupID, userID: selfInfo.userID, nickname: selfInfo.nickname, faceURL: selfInfo.faceURL, permissions: selfInfo.permissions }),
|
|
202
|
+
};
|
|
203
|
+
});
|
|
192
204
|
},
|
|
193
205
|
clearSelectedConversation: () => {
|
|
194
206
|
set({
|