@droppii-org/chat-sdk 0.6.52 → 0.6.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +8 -0
- package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
- package/dist/components/group/AdminPermissionModal.js +9 -38
- package/dist/components/group/MemberPermissionPanel.d.ts.map +1 -1
- package/dist/components/group/MemberPermissionPanel.js +45 -33
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +6 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +2 -0
- package/dist/hooks/conversation/useViewOnlyGroupRefresh.d.ts +14 -0
- package/dist/hooks/conversation/useViewOnlyGroupRefresh.d.ts.map +1 -0
- package/dist/hooks/conversation/useViewOnlyGroupRefresh.js +44 -0
- package/dist/hooks/group/useGroupMemberActions.d.ts +7 -0
- package/dist/hooks/group/useGroupMemberActions.d.ts.map +1 -1
- package/dist/hooks/group/useGroupMemberActions.js +23 -0
- package/dist/hooks/message/useMessage.d.ts +2 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +84 -0
- package/dist/hooks/search/useSearchGroups.d.ts +1 -0
- package/dist/hooks/search/useSearchGroups.d.ts.map +1 -1
- package/dist/hooks/search/useSearchGroups.js +11 -1
- package/dist/locales/vi/common.json +1 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +73 -0
- package/dist/store/type.d.ts +1 -0
- package/dist/store/type.d.ts.map +1 -1
- package/dist/utils/groupPermissionSwitches.d.ts +9 -0
- package/dist/utils/groupPermissionSwitches.d.ts.map +1 -0
- package/dist/utils/groupPermissionSwitches.js +19 -0
- package/package.json +2 -2
- package/dist/store/groupPermissionOverrides.d.ts +0 -23
- package/dist/store/groupPermissionOverrides.d.ts.map +0 -1
- package/dist/store/groupPermissionOverrides.js +0 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA0BzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,4DAI9B,4BAA4B,4CA8Q9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -12,7 +12,9 @@ import { useUnreadMessageIndicator } from "../../hooks/message/useUnreadMessageI
|
|
|
12
12
|
import { usePinMessage } from "../../hooks/message/usePinMessage";
|
|
13
13
|
import { useRevokeMessage } from "../../hooks/message/useRevokeMessage";
|
|
14
14
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
15
|
+
import { useViewOnlyGroupRefresh } from "../../hooks/conversation/useViewOnlyGroupRefresh";
|
|
15
16
|
import useConversationStore from "../../store/conversation";
|
|
17
|
+
import { PeerType } from "../../types/conversation";
|
|
16
18
|
import useAuthStore from "../../store/auth";
|
|
17
19
|
import { isGroupChat as resolveIsGroupChat, isSupportSessionAgentView, } from "../../utils/conversation";
|
|
18
20
|
import emitter from "../../utils/events";
|
|
@@ -28,6 +30,7 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
|
|
|
28
30
|
const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
|
|
29
31
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
30
32
|
const searchClientMsgID = useConversationStore((state) => state.searchClientMsgID);
|
|
33
|
+
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
31
34
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
32
35
|
const conversationId = selectedConversationId || (conversation === null || conversation === void 0 ? void 0 : conversation.conversationID) || "";
|
|
33
36
|
const isGroupChat = conversation ? resolveIsGroupChat(conversation) : false;
|
|
@@ -40,6 +43,11 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
|
|
|
40
43
|
// agent handling someone else's session needs — load the full history.
|
|
41
44
|
const useAppHistory = !isSupportSessionAgentView(Object.assign(Object.assign({}, conversation), { conversationID: conversationId }), viewerUserID);
|
|
42
45
|
const messageController = useMessage(conversationId, searchClientMsgID, true, useAppHistory, onSearchMessageRevoked);
|
|
46
|
+
useViewOnlyGroupRefresh({
|
|
47
|
+
enabled: isViewOnlyMember && (conversation === null || conversation === void 0 ? void 0 : conversation.peerType) === PeerType.GROUP,
|
|
48
|
+
groupId: conversation === null || conversation === void 0 ? void 0 : conversation.groupID,
|
|
49
|
+
refreshMessages: messageController.refreshLatestMessages,
|
|
50
|
+
});
|
|
43
51
|
const { isResolved: isUnreadLookupResolved, firstUnreadMessageId, unreadDividerMessageId, showJumpButton, markDividerSeen, } = useUnreadMessageIndicator({
|
|
44
52
|
conversationId,
|
|
45
53
|
searchClientMsgID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminPermissionModal.d.ts","sourceRoot":"","sources":["../../../src/components/group/AdminPermissionModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminPermissionModal.d.ts","sourceRoot":"","sources":["../../../src/components/group/AdminPermissionModal.tsx"],"names":[],"mappings":"AAqBA,UAAU,0BAA0B;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IAIpB,MAAM,CAAC,EAAE,OAAO,CAAC;IAGjB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IAKpC,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC5E;AAwCD,QAAA,MAAM,oBAAoB,GAAI,uEAO3B,yBAAyB,mDAgK3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -5,10 +5,11 @@ import { ConfigProvider, Modal, Switch, message } from "antd";
|
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
|
|
7
7
|
import { useGroupMembers } from "../../hooks/group/useGroupMembers";
|
|
8
|
-
import { useSetGroupMemberPermissions } from "../../hooks/group/useGroupMemberActions";
|
|
8
|
+
import { useSetAdminRolePermissions, useSetGroupMemberPermissions, } from "../../hooks/group/useGroupMemberActions";
|
|
9
9
|
import { Icon } from "../../components/icon";
|
|
10
10
|
import { ANTD_PRIMARY_COLOR_VAR } from "../../constants";
|
|
11
11
|
import { GROUP_PERMISSION_KEY } from "../../constants/im";
|
|
12
|
+
import { changedSwitches, commonSwitchStates, toRolePermissionChange, } from "../../utils/groupPermissionSwitches";
|
|
12
13
|
// "Cập nhật thông tin nhóm" is one switch in the UI but bundles 3 BE
|
|
13
14
|
// permission keys (name/avatar/description), so it maps to all three.
|
|
14
15
|
const UPDATE_GROUP_INFO_KEYS = [
|
|
@@ -24,35 +25,21 @@ const SWITCH_PERMISSION_KEYS = {
|
|
|
24
25
|
removeMember: [GROUP_PERMISSION_KEY.removeMember],
|
|
25
26
|
updateGroupInfo: UPDATE_GROUP_INFO_KEYS,
|
|
26
27
|
};
|
|
27
|
-
const SWITCHES = Object.keys(SWITCH_PERMISSION_KEYS);
|
|
28
|
-
// A switch reads on only when every given permission list grants all of its keys.
|
|
29
|
-
const commonOverrides = (permissionLists) => {
|
|
30
|
-
const grants = (name) => permissionLists.length > 0 &&
|
|
31
|
-
permissionLists.every((list) => SWITCH_PERMISSION_KEYS[name].every((key) => list.includes(key)));
|
|
32
|
-
return {
|
|
33
|
-
sendMessage: grants("sendMessage"),
|
|
34
|
-
pinMessage: grants("pinMessage"),
|
|
35
|
-
viewMemberInformation: grants("viewMemberInformation"),
|
|
36
|
-
addMember: grants("addMember"),
|
|
37
|
-
removeMember: grants("removeMember"),
|
|
38
|
-
updateGroupInfo: grants("updateGroupInfo"),
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
28
|
// Rewrites only the switches that changed; every other key in the list stays as is.
|
|
42
29
|
const applySwitchChanges = (permissionList, changes) => Object.entries(changes).reduce((list, [name, enabled]) => {
|
|
43
30
|
const keys = SWITCH_PERMISSION_KEYS[name];
|
|
44
31
|
const without = list.filter((permission) => !keys.includes(permission));
|
|
45
32
|
return enabled ? [...without, ...keys] : without;
|
|
46
33
|
}, permissionList);
|
|
47
|
-
const isSamePermissionSet = (a, b) => a.length === b.length && a.every((permission) => b.includes(permission));
|
|
48
34
|
// "Configure Admin Permissions". With `member` (a member row's … menu) it edits
|
|
49
35
|
// that one Admin. Without it (the group info panel) it edits every current
|
|
50
|
-
// Admin
|
|
51
|
-
//
|
|
36
|
+
// Admin in one setGroupAdminPermissions call: switches start from what the
|
|
37
|
+
// Admins already share, and "Lưu" sends only the switches the user flipped.
|
|
52
38
|
const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member, onMemberPermissionsSaved, }) => {
|
|
53
39
|
const { t } = useTranslation();
|
|
54
40
|
const { members: groupMembers } = useGroupMembers(member ? undefined : groupID);
|
|
55
41
|
const { mutateAsync: setMemberPermissions } = useSetGroupMemberPermissions();
|
|
42
|
+
const { mutateAsync: setAdminRolePermissions } = useSetAdminRolePermissions();
|
|
56
43
|
const [isSaving, setIsSaving] = useState(false);
|
|
57
44
|
const [edits, setEdits] = useState({});
|
|
58
45
|
useEffect(() => {
|
|
@@ -60,19 +47,14 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member,
|
|
|
60
47
|
setEdits({});
|
|
61
48
|
}, [open, member === null || member === void 0 ? void 0 : member.userID]);
|
|
62
49
|
const admins = groupMembers.filter((groupMember) => groupMember.roleLevel === GroupMemberRole.Admin);
|
|
63
|
-
const baseline = member
|
|
64
|
-
? commonOverrides([member.permissions])
|
|
65
|
-
: commonOverrides(admins.map((admin) => { var _a; return (_a = admin.permissions) !== null && _a !== void 0 ? _a : []; }));
|
|
50
|
+
const baseline = commonSwitchStates(SWITCH_PERMISSION_KEYS, member ? [member.permissions] : admins.map((admin) => { var _a; return (_a = admin.permissions) !== null && _a !== void 0 ? _a : []; }));
|
|
66
51
|
const pending = Object.assign(Object.assign({}, baseline), edits);
|
|
67
|
-
const changes =
|
|
68
|
-
name,
|
|
69
|
-
pending[name],
|
|
70
|
-
]));
|
|
52
|
+
const changes = changedSwitches(baseline, pending);
|
|
71
53
|
const hasChanges = Object.keys(changes).length > 0;
|
|
72
54
|
const setSwitch = (name, checked) => setEdits((prev) => (Object.assign(Object.assign({}, prev), { [name]: checked })));
|
|
73
55
|
const handleSave = async () => {
|
|
74
56
|
var _a, _b;
|
|
75
|
-
if (!groupID)
|
|
57
|
+
if (!groupID || !hasChanges)
|
|
76
58
|
return;
|
|
77
59
|
setIsSaving(true);
|
|
78
60
|
try {
|
|
@@ -86,18 +68,7 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member,
|
|
|
86
68
|
onMemberPermissionsSaved === null || onMemberPermissionsSaved === void 0 ? void 0 : onMemberPermissionsSaved(member.userID, nextPermissions);
|
|
87
69
|
}
|
|
88
70
|
else {
|
|
89
|
-
await
|
|
90
|
-
var _a;
|
|
91
|
-
const current = (_a = admin.permissions) !== null && _a !== void 0 ? _a : [];
|
|
92
|
-
const nextPermissions = applySwitchChanges(current, changes);
|
|
93
|
-
if (isSamePermissionSet(current, nextPermissions))
|
|
94
|
-
return undefined;
|
|
95
|
-
return setMemberPermissions({
|
|
96
|
-
groupID,
|
|
97
|
-
userID: admin.userID,
|
|
98
|
-
permissions: nextPermissions,
|
|
99
|
-
});
|
|
100
|
-
}));
|
|
71
|
+
await setAdminRolePermissions(Object.assign({ groupID }, toRolePermissionChange(SWITCH_PERMISSION_KEYS, changes)));
|
|
101
72
|
}
|
|
102
73
|
message.success(t("update_admin_permissions_success"));
|
|
103
74
|
onClose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemberPermissionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/group/MemberPermissionPanel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MemberPermissionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/group/MemberPermissionPanel.tsx"],"names":[],"mappings":"AAkBA,UAAU,0BAA0B;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAuBD,QAAA,MAAM,qBAAqB,GAAI,4BAA4B,0BAA0B,mDA2FpF,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -3,51 +3,63 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { ConfigProvider, Switch, message } from "antd";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
|
-
import {
|
|
6
|
+
import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
|
|
7
|
+
import { useGroupMembers } from "../../hooks/group/useGroupMembers";
|
|
8
|
+
import { useSetMemberRolePermissions } from "../../hooks/group/useGroupMemberActions";
|
|
7
9
|
import { Icon } from "../../components/icon";
|
|
8
10
|
import { ANTD_PRIMARY_COLOR_VAR } from "../../constants";
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
import { GROUP_PERMISSION_KEY } from "../../constants/im";
|
|
12
|
+
import { changedSwitches, commonSwitchStates, toRolePermissionChange, } from "../../utils/groupPermissionSwitches";
|
|
13
|
+
const SWITCH_PERMISSION_KEYS = {
|
|
14
|
+
sendMessage: [GROUP_PERMISSION_KEY.sendMessage],
|
|
15
|
+
addMember: [GROUP_PERMISSION_KEY.addMember],
|
|
16
|
+
pinMessage: [GROUP_PERMISSION_KEY.pinMessage],
|
|
17
|
+
viewMemberInformation: [GROUP_PERMISSION_KEY.viewMemberInformation],
|
|
18
|
+
};
|
|
19
|
+
const SWITCH_LABELS = {
|
|
20
|
+
sendMessage: "permission_send_message",
|
|
21
|
+
addMember: "permission_add_member",
|
|
22
|
+
pinMessage: "permission_pin_message",
|
|
23
|
+
viewMemberInformation: "permission_view_member_info",
|
|
24
|
+
};
|
|
25
|
+
const SWITCHES = Object.keys(SWITCH_PERMISSION_KEYS);
|
|
26
|
+
// "Configure Member Permissions", inline in the "Thông tin" sidebar. Switches
|
|
27
|
+
// start from what every ordinary member already shares, and "Lưu" sends only
|
|
28
|
+
// the switches the user flipped, in one setGroupMemberPermissions call.
|
|
14
29
|
const MemberPermissionPanel = ({ open, groupID, onClose }) => {
|
|
15
30
|
const { t } = useTranslation();
|
|
16
|
-
const
|
|
17
|
-
const
|
|
31
|
+
const { members: groupMembers, isLoading } = useGroupMembers(open ? groupID : undefined);
|
|
32
|
+
const { mutateAsync: setMemberRolePermissions } = useSetMemberRolePermissions();
|
|
18
33
|
const [isSaving, setIsSaving] = useState(false);
|
|
19
|
-
const [
|
|
20
|
-
sendMessage: permissions.sendMessage,
|
|
21
|
-
addMember: permissions.addMember,
|
|
22
|
-
pinMessage: permissions.pinMessage,
|
|
23
|
-
viewMemberInformation: permissions.viewMemberInformation,
|
|
24
|
-
});
|
|
34
|
+
const [edits, setEdits] = useState({});
|
|
25
35
|
useEffect(() => {
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
setPending({
|
|
29
|
-
sendMessage: permissions.sendMessage,
|
|
30
|
-
addMember: permissions.addMember,
|
|
31
|
-
pinMessage: permissions.pinMessage,
|
|
32
|
-
viewMemberInformation: permissions.viewMemberInformation,
|
|
33
|
-
});
|
|
34
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
+
if (open)
|
|
37
|
+
setEdits({});
|
|
35
38
|
}, [open]);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const members = groupMembers.filter((groupMember) => groupMember.roleLevel === GroupMemberRole.Normal);
|
|
40
|
+
const baseline = commonSwitchStates(SWITCH_PERMISSION_KEYS, members.map((member) => { var _a; return (_a = member.permissions) !== null && _a !== void 0 ? _a : []; }));
|
|
41
|
+
const pending = Object.assign(Object.assign({}, baseline), edits);
|
|
42
|
+
const changes = changedSwitches(baseline, pending);
|
|
43
|
+
const hasChanges = Object.keys(changes).length > 0;
|
|
40
44
|
const handleSave = async () => {
|
|
41
|
-
if (!groupID)
|
|
45
|
+
if (!groupID || !hasChanges)
|
|
42
46
|
return;
|
|
43
47
|
setIsSaving(true);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
try {
|
|
49
|
+
await setMemberRolePermissions(Object.assign({ groupID }, toRolePermissionChange(SWITCH_PERMISSION_KEYS, changes)));
|
|
50
|
+
message.success(t("update_member_permissions_success"));
|
|
51
|
+
onClose();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error("setGroupMemberPermissions", error);
|
|
55
|
+
message.error(t("update_member_permissions_failed"));
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setIsSaving(false);
|
|
59
|
+
}
|
|
48
60
|
};
|
|
49
61
|
if (!open)
|
|
50
62
|
return null;
|
|
51
|
-
return (_jsxs("div", { className: "absolute inset-0 bg-white flex flex-col z-10", "data-cp-name": "MemberPermissionPanel", children: [_jsxs("div", { className: "flex w-full items-center border-b border-gray-100 p-2", children: [_jsx("button", { type: "button", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-[var(--dc-color-nav-icon)]", onClick: onClose, children: _jsx(Icon, { icon: "arrow-left-o", size: 24 }) }), _jsx("p", { className: "min-w-0 flex-1 truncate px-2 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("member_permission_settings") })] }), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsx(ConfigProvider, { theme: { token: { colorPrimary: ANTD_PRIMARY_COLOR_VAR } }, children:
|
|
63
|
+
return (_jsxs("div", { className: "absolute inset-0 bg-white flex flex-col z-10", "data-cp-name": "MemberPermissionPanel", children: [_jsxs("div", { className: "flex w-full items-center border-b border-gray-100 p-2", children: [_jsx("button", { type: "button", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-[var(--dc-color-nav-icon)]", onClick: onClose, children: _jsx(Icon, { icon: "arrow-left-o", size: 24 }) }), _jsx("p", { className: "min-w-0 flex-1 truncate px-2 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("member_permission_settings") })] }), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsx(ConfigProvider, { theme: { token: { colorPrimary: ANTD_PRIMARY_COLOR_VAR } }, children: _jsx("div", { className: "flex flex-col py-2", children: SWITCHES.map((name) => (_jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t(SWITCH_LABELS[name]) }), _jsx(Switch, { checked: pending[name], disabled: isLoading, onChange: (checked) => setEdits((prev) => (Object.assign(Object.assign({}, prev), { [name]: checked }))) })] }, name))) }) }) }), _jsxs("div", { className: "flex items-center justify-end gap-2 px-4 py-3 border-t border-gray-100", children: [_jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full border border-gray-300 text-gray-700 text-sm font-medium", onClick: onClose, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full bg-[var(--dc-color-primary)] text-white text-sm font-medium disabled:bg-gray-200 disabled:text-gray-400", disabled: !hasChanges || isSaving || isLoading, onClick: handleSave, children: t("save") })] })] }));
|
|
52
64
|
};
|
|
53
65
|
export default MemberPermissionPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAqCA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CA2Z3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -24,6 +24,7 @@ import { usePinMessage } from "../../hooks/message/usePinMessage";
|
|
|
24
24
|
import { useConversationSessionState } from "../../hooks/session/useConversationSessionState";
|
|
25
25
|
import { useJoinGroupFlow } from "../../hooks/session/useJoinGroupFlow";
|
|
26
26
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
27
|
+
import { useViewOnlyGroupRefresh } from "../../hooks/conversation/useViewOnlyGroupRefresh";
|
|
27
28
|
import { isGroupConversation } from "../../utils/imCommon";
|
|
28
29
|
import { ConnectStatus, SessionStatus } from "../../types/chat";
|
|
29
30
|
import { PeerType } from "../../types/conversation";
|
|
@@ -108,6 +109,11 @@ const MessageList = (props) => {
|
|
|
108
109
|
}
|
|
109
110
|
}, [isConfirmedGroup, isJoined, isViewOnlyMember, setIsViewOnlyMember]);
|
|
110
111
|
const messageController = useMessage(conversationId, searchClientMsgID, true, false, onSearchMessageRevoked);
|
|
112
|
+
useViewOnlyGroupRefresh({
|
|
113
|
+
enabled: isViewOnlyMember && isConfirmedGroup,
|
|
114
|
+
groupId: groupID,
|
|
115
|
+
refreshMessages: messageController.refreshLatestMessages,
|
|
116
|
+
});
|
|
111
117
|
const { loadState, moreOldLoading, moreNewLoading } = messageController;
|
|
112
118
|
const { isResolved: isUnreadLookupResolved, firstUnreadMessageId, unreadDividerMessageId, showJumpButton, markDividerSeen, } = useUnreadMessageIndicator({
|
|
113
119
|
conversationId,
|
|
@@ -5,6 +5,7 @@ export declare const PAGE_SIZE: {
|
|
|
5
5
|
readonly medium: 20;
|
|
6
6
|
readonly large: 50;
|
|
7
7
|
};
|
|
8
|
+
export declare const VIEW_ONLY_REFRESH_INTERVAL_MS = 10000;
|
|
8
9
|
export declare const MSG_ITEM_PREFIX = "msg-item-";
|
|
9
10
|
export declare const MSG_ITEM_CONTENT_PREFIX = "msg-item-content-";
|
|
10
11
|
export declare const MSG_ITEM_BUBBLE_GROUP_PREFIX = "msg-item-bubble-group-";
|
|
@@ -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;
|
|
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;AAGX,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAE3D,eAAO,MAAM,4BAA4B,2BAA2B,CAAC;AAMrE,eAAO,MAAM,sBAAsB,4BAA4B,CAAC"}
|
package/dist/constants/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export const PAGE_SIZE = {
|
|
|
5
5
|
medium: 20,
|
|
6
6
|
large: 50,
|
|
7
7
|
};
|
|
8
|
+
// A non-member viewing a public group gets no pushes, so the view polls at this pace.
|
|
9
|
+
export const VIEW_ONLY_REFRESH_INTERVAL_MS = 10000;
|
|
8
10
|
export const MSG_ITEM_PREFIX = "msg-item-";
|
|
9
11
|
export const MSG_ITEM_CONTENT_PREFIX = "msg-item-content-";
|
|
10
12
|
// Bubble plus its pin badge, so the jump-to-message pulse moves both together.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface UseViewOnlyGroupRefreshOptions {
|
|
2
|
+
/** True only while the viewer reads a public group they are not a member of. */
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
groupId?: string;
|
|
5
|
+
refreshMessages: () => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* OpenIM only pushes to group members, so a read-only viewer would keep the
|
|
9
|
+
* snapshot from when they opened the group. Poll new messages and group info
|
|
10
|
+
* while the tab is visible, and once right away when it becomes visible again.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useViewOnlyGroupRefresh: ({ enabled, groupId, refreshMessages, }: UseViewOnlyGroupRefreshOptions) => void;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=useViewOnlyGroupRefresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useViewOnlyGroupRefresh.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useViewOnlyGroupRefresh.ts"],"names":[],"mappings":"AAKA,UAAU,8BAA8B;IACtC,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,wCAIrC,8BAA8B,SAgChC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useLatest } from "ahooks";
|
|
3
|
+
import { VIEW_ONLY_REFRESH_INTERVAL_MS } from "../../constants";
|
|
4
|
+
import useConversationStore from "../../store/conversation";
|
|
5
|
+
/**
|
|
6
|
+
* OpenIM only pushes to group members, so a read-only viewer would keep the
|
|
7
|
+
* snapshot from when they opened the group. Poll new messages and group info
|
|
8
|
+
* while the tab is visible, and once right away when it becomes visible again.
|
|
9
|
+
*/
|
|
10
|
+
export const useViewOnlyGroupRefresh = ({ enabled, groupId, refreshMessages, }) => {
|
|
11
|
+
const refreshMessagesRef = useLatest(refreshMessages);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!enabled || !groupId)
|
|
14
|
+
return;
|
|
15
|
+
let inFlight = false;
|
|
16
|
+
const refresh = async () => {
|
|
17
|
+
var _a;
|
|
18
|
+
if (inFlight || document.visibilityState === "hidden")
|
|
19
|
+
return;
|
|
20
|
+
inFlight = true;
|
|
21
|
+
try {
|
|
22
|
+
await Promise.allSettled([
|
|
23
|
+
(_a = refreshMessagesRef.current) === null || _a === void 0 ? void 0 : _a.call(refreshMessagesRef),
|
|
24
|
+
useConversationStore.getState().refreshViewOnlyGroup(groupId),
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
inFlight = false;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const onVisibilityChange = () => {
|
|
32
|
+
if (document.visibilityState === "visible")
|
|
33
|
+
void refresh();
|
|
34
|
+
};
|
|
35
|
+
const timer = window.setInterval(() => {
|
|
36
|
+
void refresh();
|
|
37
|
+
}, VIEW_ONLY_REFRESH_INTERVAL_MS);
|
|
38
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
39
|
+
return () => {
|
|
40
|
+
window.clearInterval(timer);
|
|
41
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
42
|
+
};
|
|
43
|
+
}, [enabled, groupId, refreshMessagesRef]);
|
|
44
|
+
};
|
|
@@ -27,5 +27,12 @@ interface SetGroupMemberPermissionsParams {
|
|
|
27
27
|
permissions: string[];
|
|
28
28
|
}
|
|
29
29
|
export declare const useSetGroupMemberPermissions: () => import("@tanstack/react-query").UseMutationResult<unknown, Error, SetGroupMemberPermissionsParams, unknown>;
|
|
30
|
+
interface SetRolePermissionsParams {
|
|
31
|
+
groupID: string;
|
|
32
|
+
enablePermissions: string[];
|
|
33
|
+
disablePermissions: string[];
|
|
34
|
+
}
|
|
35
|
+
export declare const useSetAdminRolePermissions: () => import("@tanstack/react-query").UseMutationResult<unknown, Error, SetRolePermissionsParams, unknown>;
|
|
36
|
+
export declare const useSetMemberRolePermissions: () => import("@tanstack/react-query").UseMutationResult<unknown, Error, SetRolePermissionsParams, unknown>;
|
|
30
37
|
export {};
|
|
31
38
|
//# sourceMappingURL=useGroupMemberActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGroupMemberActions.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useGroupMemberActions.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAmB,MAAM,8BAA8B,CAAC;AAQhF,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,eAAO,MAAM,kBAAkB,yGAW9B,CAAC;AAEF,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,yGAW9B,CAAC;AAEF,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,eAAO,MAAM,qBAAqB,4GAWjC,CAAC;AAEF,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,qBAAqB,4GAUjC,CAAC;AAEF,UAAU,+BAA+B;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,eAAO,MAAM,4BAA4B,mHAexC,CAAC"}
|
|
1
|
+
{"version":3,"file":"useGroupMemberActions.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useGroupMemberActions.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAmB,MAAM,8BAA8B,CAAC;AAQhF,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,eAAO,MAAM,kBAAkB,yGAW9B,CAAC;AAEF,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,yGAW9B,CAAC;AAEF,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,eAAO,MAAM,qBAAqB,4GAWjC,CAAC;AAEF,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,qBAAqB,4GAUjC,CAAC;AAEF,UAAU,+BAA+B;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,eAAO,MAAM,4BAA4B,mHAexC,CAAC;AAEF,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAaD,eAAO,MAAM,0BAA0B,4GAOtC,CAAC;AAGF,eAAO,MAAM,2BAA2B,4GAOvC,CAAC"}
|
|
@@ -64,3 +64,26 @@ export const useSetGroupMemberPermissions = () => {
|
|
|
64
64
|
},
|
|
65
65
|
});
|
|
66
66
|
};
|
|
67
|
+
const toRolePermissionsReq = ({ groupID, enablePermissions, disablePermissions, }) => ({
|
|
68
|
+
groupID,
|
|
69
|
+
enablePermissions: enablePermissions,
|
|
70
|
+
disablePermissions: disablePermissions,
|
|
71
|
+
});
|
|
72
|
+
// One call for every Admin of the group; each Admin keeps the keys not listed.
|
|
73
|
+
export const useSetAdminRolePermissions = () => {
|
|
74
|
+
return useMutation({
|
|
75
|
+
mutationFn: async (params) => {
|
|
76
|
+
const { data } = await DChatSDK.setGroupAdminPermissions(toRolePermissionsReq(params));
|
|
77
|
+
return data;
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
// One call for every ordinary member of the group; each keeps the keys not listed.
|
|
82
|
+
export const useSetMemberRolePermissions = () => {
|
|
83
|
+
return useMutation({
|
|
84
|
+
mutationFn: async (params) => {
|
|
85
|
+
const { data } = await DChatSDK.setGroupMemberPermissions(toRolePermissionsReq(params));
|
|
86
|
+
return data;
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
};
|
|
@@ -22,10 +22,12 @@ export declare const useMessage: (conversationId?: string, searchClientMsgID?: s
|
|
|
22
22
|
getMoreOldMessages: (loadMore?: any) => Promise<void>;
|
|
23
23
|
moreNewLoading: boolean;
|
|
24
24
|
getMoreNewMessages: (loadMore?: any) => Promise<void>;
|
|
25
|
+
refreshLatestMessages: () => Promise<void>;
|
|
25
26
|
};
|
|
26
27
|
export declare const pushNewMessage: (message: MessageItem) => void;
|
|
27
28
|
export declare const updateOneMessage: (message: MessageItem) => void;
|
|
28
29
|
export declare const removeOneMessage: (clientMsgID: string) => void;
|
|
29
30
|
export declare const getVisibleNeighbor: (allMessages: MessageItem[], current: MessageItem, direction: "prev" | "next") => MessageItem | undefined;
|
|
30
31
|
export declare const getImmediateNeighbor: (allMessages: MessageItem[], current: MessageItem, direction: "prev" | "next") => MessageItem | undefined;
|
|
32
|
+
export declare const mergeLatestMessages: (current: MessageItem[], fetched: MessageItem[]) => MessageItem[];
|
|
31
33
|
//# sourceMappingURL=useMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAgBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA0BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAgBd,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE
|
|
1
|
+
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAgBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA0BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAgBd,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;CAoZnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAkBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAmBF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
|
|
@@ -242,6 +242,67 @@ onSearchMessageRevoked) => {
|
|
|
242
242
|
}, {
|
|
243
243
|
manual: true,
|
|
244
244
|
});
|
|
245
|
+
// For a viewer who gets no pushes (non-member of a public group): re-read the
|
|
246
|
+
// newest page(s), walking back until they overlap the shown list so a burst of
|
|
247
|
+
// new messages never leaves a gap.
|
|
248
|
+
const pageLoadingRef = useLatest(moreOldLoading || moreNewLoading);
|
|
249
|
+
const refreshLatestMessages = async () => {
|
|
250
|
+
const state = latestLoadState.current;
|
|
251
|
+
// A jumped-to window (hasMoreNew) hides the newest messages on purpose; a
|
|
252
|
+
// page load in flight would race the merge below, so wait for the next poll.
|
|
253
|
+
if (!state ||
|
|
254
|
+
!canLoadRef.current ||
|
|
255
|
+
state.initLoading ||
|
|
256
|
+
state.hasMoreNew ||
|
|
257
|
+
pageLoadingRef.current) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const reqConversationID = conversationId;
|
|
261
|
+
const reqUseAppHistory = useAppHistory;
|
|
262
|
+
if (!reqConversationID)
|
|
263
|
+
return;
|
|
264
|
+
const shown = new Set(state.messageList.map((msg) => msg.clientMsgID));
|
|
265
|
+
const fetched = [];
|
|
266
|
+
let bridged = shown.size === 0;
|
|
267
|
+
let startClientMsgID = "";
|
|
268
|
+
for (let page = 0; page < MAX_REFRESH_PAGES; page++) {
|
|
269
|
+
const params = {
|
|
270
|
+
count: PAGE_SIZE.large,
|
|
271
|
+
startClientMsgID,
|
|
272
|
+
conversationID: reqConversationID,
|
|
273
|
+
viewType: ViewType.History,
|
|
274
|
+
};
|
|
275
|
+
const { data } = reqUseAppHistory
|
|
276
|
+
? await DChatSDK.getAdvancedHistoryMessageListApp(params)
|
|
277
|
+
: await DChatSDK.getAdvancedHistoryMessageList(params);
|
|
278
|
+
if (isStaleResponse(reqConversationID, reqUseAppHistory))
|
|
279
|
+
return;
|
|
280
|
+
const newestFirst = data.messageList.toReversed();
|
|
281
|
+
fetched.push(...newestFirst);
|
|
282
|
+
if (newestFirst.some((msg) => shown.has(msg.clientMsgID))) {
|
|
283
|
+
bridged = true;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
if (data.isEnd || newestFirst.length === 0) {
|
|
287
|
+
bridged = true;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
startClientMsgID = newestFirst[newestFirst.length - 1].clientMsgID;
|
|
291
|
+
}
|
|
292
|
+
setLoadState((preState) => {
|
|
293
|
+
// More new messages than we walked back: show the fresh pages alone
|
|
294
|
+
// rather than a list with a hole in it; older ones load on scroll.
|
|
295
|
+
if (!bridged) {
|
|
296
|
+
if (pageLoadingRef.current)
|
|
297
|
+
return preState;
|
|
298
|
+
return Object.assign(Object.assign({}, preState), { hasMoreOld: true, messageList: fetched });
|
|
299
|
+
}
|
|
300
|
+
const messageList = mergeLatestMessages(preState.messageList, fetched);
|
|
301
|
+
return messageList === preState.messageList
|
|
302
|
+
? preState
|
|
303
|
+
: Object.assign(Object.assign({}, preState), { messageList });
|
|
304
|
+
});
|
|
305
|
+
};
|
|
245
306
|
useEffect(() => {
|
|
246
307
|
const pushNewMessage = (message) => {
|
|
247
308
|
var _a, _b;
|
|
@@ -335,6 +396,7 @@ onSearchMessageRevoked) => {
|
|
|
335
396
|
getMoreOldMessages,
|
|
336
397
|
moreNewLoading,
|
|
337
398
|
getMoreNewMessages,
|
|
399
|
+
refreshLatestMessages,
|
|
338
400
|
};
|
|
339
401
|
};
|
|
340
402
|
export const pushNewMessage = (message) => emit("PUSH_NEW_MSG", message);
|
|
@@ -376,6 +438,28 @@ const removeDuplicateMessages = (messages) => {
|
|
|
376
438
|
return true;
|
|
377
439
|
});
|
|
378
440
|
};
|
|
441
|
+
// How far refreshLatestMessages walks back to meet the shown list.
|
|
442
|
+
const MAX_REFRESH_PAGES = 3;
|
|
443
|
+
// Server copies replace shown ones whose type changed (a revoke becomes its
|
|
444
|
+
// placeholder); new ones are placed by sendTime like pushNewMessage does.
|
|
445
|
+
// Returns `current` itself when nothing changed, so React can skip the render.
|
|
446
|
+
export const mergeLatestMessages = (current, fetched) => {
|
|
447
|
+
const unseen = new Map(fetched.map((msg) => [msg.clientMsgID, msg]));
|
|
448
|
+
let changed = false;
|
|
449
|
+
const kept = current.map((msg) => {
|
|
450
|
+
const fresh = unseen.get(msg.clientMsgID);
|
|
451
|
+
if (!fresh)
|
|
452
|
+
return msg;
|
|
453
|
+
unseen.delete(msg.clientMsgID);
|
|
454
|
+
if (fresh.contentType === msg.contentType)
|
|
455
|
+
return msg;
|
|
456
|
+
changed = true;
|
|
457
|
+
return Object.assign(Object.assign({}, msg), fresh);
|
|
458
|
+
});
|
|
459
|
+
if (unseen.size === 0 && !changed)
|
|
460
|
+
return current;
|
|
461
|
+
return [...unseen.values(), ...kept].sort((a, b) => (b.sendTime || 0) - (a.sendTime || 0));
|
|
462
|
+
};
|
|
379
463
|
// Merges `base` with `incoming`, keeping each message at the position of its
|
|
380
464
|
// first occurrence but letting `incoming` (the later/authoritative source,
|
|
381
465
|
// e.g. a fresh server fetch) overwrite the content of any duplicate.
|
|
@@ -7,6 +7,7 @@ interface UseSearchGroupsParams {
|
|
|
7
7
|
pageSize?: number;
|
|
8
8
|
scope?: SearchScope;
|
|
9
9
|
}
|
|
10
|
+
export declare const buildSearchedGroupConversation: (group: IConversationGroupItem) => import("@droppii-org/wasm-client-sdk").ConversationItem;
|
|
10
11
|
export declare const useSearchGroups: ({ searchTerm, enabled, pageSize, scope, }: UseSearchGroupsParams) => {
|
|
11
12
|
groups: ConversationGroupDisplayItem[];
|
|
12
13
|
fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<BaseResponse<IConversationGroupItem[]>, unknown>, Error>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSearchGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchGroups.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"useSearchGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchGroups.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,UAAU,qBAAqB;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAID,eAAO,MAAM,8BAA8B,GAAI,OAAO,sBAAsB,4DAQ3E,CAAC;AA4BF,eAAO,MAAM,eAAe,GAAI,2CAK7B,qBAAqB;;;;;;CAmEvB,CAAC"}
|
|
@@ -8,6 +8,16 @@ import { PAGE_SIZE } from "../../constants";
|
|
|
8
8
|
import useAuthStore from "../../store/auth";
|
|
9
9
|
import useConversationStore from "../../store/conversation";
|
|
10
10
|
import { buildFallbackConversation, syncGroupConversationsToStore, } from "../../hooks/group/useConversationGroups";
|
|
11
|
+
import { PeerType } from "../../types/conversation";
|
|
12
|
+
// A searched group the viewer isn't in never syncs, so this is all it gets. Real
|
|
13
|
+
// groups carry visibility; mark them so the view-only/block checks run on open.
|
|
14
|
+
export const buildSearchedGroupConversation = (group) => {
|
|
15
|
+
const conversation = buildFallbackConversation(group);
|
|
16
|
+
const isRealGroup = group.visibility !== undefined &&
|
|
17
|
+
!group.conversationId.startsWith("sg_bot_");
|
|
18
|
+
return isRealGroup
|
|
19
|
+
? Object.assign(Object.assign({}, conversation), { peerType: PeerType.GROUP }) : conversation;
|
|
20
|
+
};
|
|
11
21
|
const fetchGroupsPage = (scope, page, pageSize, searchTerm) => {
|
|
12
22
|
if (scope === "pdp-portal") {
|
|
13
23
|
// Joined groups only, already limited to the PDP account by BE.
|
|
@@ -60,7 +70,7 @@ export const useSearchGroups = ({ searchTerm, enabled = true, pageSize = PAGE_SI
|
|
|
60
70
|
return {
|
|
61
71
|
groupId: group.groupId,
|
|
62
72
|
conversationId: group.conversationId,
|
|
63
|
-
conversation: syncedConversation !== null && syncedConversation !== void 0 ? syncedConversation :
|
|
73
|
+
conversation: syncedConversation !== null && syncedConversation !== void 0 ? syncedConversation : buildSearchedGroupConversation(group),
|
|
64
74
|
visibility: group.visibility,
|
|
65
75
|
memberCount: group.memberCount,
|
|
66
76
|
};
|
|
@@ -253,6 +253,7 @@
|
|
|
253
253
|
"permission_remove_member": "Xoá thành viên",
|
|
254
254
|
"permission_update_group_info": "Cập nhật thông tin nhóm",
|
|
255
255
|
"update_member_permissions_success": "Cập nhật quyền thành viên thành công",
|
|
256
|
+
"update_member_permissions_failed": "Cập nhật quyền thành viên thất bại",
|
|
256
257
|
"update_admin_permissions_success": "Cập nhật quyền quản trị viên thành công",
|
|
257
258
|
"invite_via_link": "Mời vào nhóm bằng link",
|
|
258
259
|
"invite_link_empty_owner_title": "Tạo link tham gia nhóm",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAUF,eAAO,MAAM,cAAc,GAAI,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,SAEhE,CAAC;AAEF,QAAA,MAAM,oBAAoB,gFAuhBvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GroupStatus, GroupVisibility, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
2
2
|
import { create } from "zustand";
|
|
3
|
+
import isEqual from "lodash/isEqual";
|
|
3
4
|
import { DChatSDK } from "../constants/sdk";
|
|
4
5
|
import { conversationSort } from "../utils/imCommon";
|
|
5
6
|
import { PeerType } from "../types/conversation";
|
|
@@ -411,6 +412,78 @@ const useConversationStore = create((set, get) => ({
|
|
|
411
412
|
setSelfLeftGroupID: (groupID) => set({ selfLeftGroupID: groupID }),
|
|
412
413
|
isViewOnlyMember: false,
|
|
413
414
|
setIsViewOnlyMember: (value) => set({ isViewOnlyMember: value }),
|
|
415
|
+
refreshViewOnlyGroup: async (groupID) => {
|
|
416
|
+
var _a;
|
|
417
|
+
let group;
|
|
418
|
+
let isJoined = false;
|
|
419
|
+
try {
|
|
420
|
+
const [{ data }, joined] = await Promise.all([
|
|
421
|
+
DChatSDK.getSpecifiedGroupsInfo([groupID]),
|
|
422
|
+
DChatSDK.isJoinGroup(groupID)
|
|
423
|
+
.then((result) => Boolean(result.data))
|
|
424
|
+
.catch(() => false),
|
|
425
|
+
]);
|
|
426
|
+
group = data === null || data === void 0 ? void 0 : data[0];
|
|
427
|
+
isJoined = joined;
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
// A failed poll proves nothing about the group; keep the last data.
|
|
431
|
+
console.error("refreshViewOnlyGroup", error);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const { conversationData, isViewOnlyMember } = get();
|
|
435
|
+
// Joined, switched thread or closed while the request was in flight.
|
|
436
|
+
if (!isViewOnlyMember || (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) !== groupID)
|
|
437
|
+
return;
|
|
438
|
+
if (!group || group.status === GroupStatus.Dismissed) {
|
|
439
|
+
get().clearSelectedConversation();
|
|
440
|
+
get().showRemovedFromGroupModal();
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
// Being added by an admin can sync as a group update instead of
|
|
444
|
+
// OnJoinedGroupAdded (the row was cached while viewing), so catch it here.
|
|
445
|
+
// Member mode waits for the member record; until then the next poll retries.
|
|
446
|
+
if (isJoined) {
|
|
447
|
+
set({ nonMemberGroupID: null });
|
|
448
|
+
await get().getCurrentMemberInGroupByReq(groupID);
|
|
449
|
+
const latestState = get();
|
|
450
|
+
if (!latestState.isViewOnlyMember ||
|
|
451
|
+
((_a = latestState.conversationData) === null || _a === void 0 ? void 0 : _a.groupID) !== groupID) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (!latestState.currentMemberInGroup.has(groupID)) {
|
|
455
|
+
// Not a member yet after all: put the kick-guard back.
|
|
456
|
+
set({ nonMemberGroupID: groupID });
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
set({ isViewOnlyMember: false });
|
|
460
|
+
// Not awaited: the self-permission call can stall right after a join.
|
|
461
|
+
void get().getSelfGroupPermissionByReq(groupID);
|
|
462
|
+
void get().getCurrentGroupInfoByReq(groupID);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (group.visibility !== GroupVisibility.Public) {
|
|
466
|
+
get().clearSelectedConversation();
|
|
467
|
+
get().showRemovedFromGroupModal();
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const latest = group;
|
|
471
|
+
set((state) => {
|
|
472
|
+
const isSameInfo = isEqual(state.currentGroupInfo.get(groupID), latest);
|
|
473
|
+
const isSameHeader = conversationData.showName === latest.groupName &&
|
|
474
|
+
conversationData.faceURL === latest.faceURL;
|
|
475
|
+
if (isSameInfo && isSameHeader)
|
|
476
|
+
return state;
|
|
477
|
+
return Object.assign({
|
|
478
|
+
// Set directly: getCurrentGroupInfoByReq's kick-guard skips groups the
|
|
479
|
+
// viewer has left, which is exactly who reaches this view.
|
|
480
|
+
currentGroupInfo: new Map(state.currentGroupInfo).set(groupID, Object.assign({}, latest)) }, (isSameHeader
|
|
481
|
+
? {}
|
|
482
|
+
: {
|
|
483
|
+
conversationData: Object.assign(Object.assign({}, conversationData), { showName: latest.groupName, faceURL: latest.faceURL }),
|
|
484
|
+
}));
|
|
485
|
+
});
|
|
486
|
+
},
|
|
414
487
|
nonMemberGroupID: null,
|
|
415
488
|
setNonMemberGroupID: (groupID) => set({ nonMemberGroupID: groupID }),
|
|
416
489
|
disbandedGroupID: null,
|
package/dist/store/type.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface ConversationStore {
|
|
|
96
96
|
setSelfLeftGroupID: (groupID: string | null) => void;
|
|
97
97
|
isViewOnlyMember: boolean;
|
|
98
98
|
setIsViewOnlyMember: (value: boolean) => void;
|
|
99
|
+
refreshViewOnlyGroup: (groupID: string) => Promise<void>;
|
|
99
100
|
nonMemberGroupID: string | null;
|
|
100
101
|
setNonMemberGroupID: (groupID: string | null) => void;
|
|
101
102
|
disbandedGroupID: string | null;
|
package/dist/store/type.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/store/type.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/store/type.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type PermissionSwitchKeys<S extends string> = Record<S, readonly string[]>;
|
|
2
|
+
export type SwitchStates<S extends string> = Record<S, boolean>;
|
|
3
|
+
export declare const commonSwitchStates: <S extends string>(switchKeys: PermissionSwitchKeys<S>, permissionLists: string[][]) => SwitchStates<S>;
|
|
4
|
+
export declare const changedSwitches: <S extends string>(baseline: SwitchStates<S>, pending: SwitchStates<S>) => Partial<SwitchStates<S>>;
|
|
5
|
+
export declare const toRolePermissionChange: <S extends string>(switchKeys: PermissionSwitchKeys<S>, changes: Partial<SwitchStates<S>>) => {
|
|
6
|
+
enablePermissions: string[];
|
|
7
|
+
disablePermissions: string[];
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=groupPermissionSwitches.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupPermissionSwitches.d.ts","sourceRoot":"","sources":["../../src/utils/groupPermissionSwitches.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAClF,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAGhE,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,EACjD,YAAY,oBAAoB,CAAC,CAAC,CAAC,EACnC,iBAAiB,MAAM,EAAE,EAAE,KAC1B,YAAY,CAAC,CAAC,CASK,CAAC;AAGvB,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,EAC9C,UAAU,YAAY,CAAC,CAAC,CAAC,EACzB,SAAS,YAAY,CAAC,CAAC,CAAC,KACvB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAKK,CAAC;AAGhC,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,MAAM,EACrD,YAAY,oBAAoB,CAAC,CAAC,CAAC,EACnC,SAAS,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;;;CAQlC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A switch reads on only when every given permission list grants all of its keys.
|
|
2
|
+
export const commonSwitchStates = (switchKeys, permissionLists) => Object.fromEntries(Object.keys(switchKeys).map((name) => [
|
|
3
|
+
name,
|
|
4
|
+
permissionLists.length > 0 &&
|
|
5
|
+
permissionLists.every((list) => switchKeys[name].every((key) => list.includes(key))),
|
|
6
|
+
]));
|
|
7
|
+
// The switches whose pending value differs from the baseline, with their new value.
|
|
8
|
+
export const changedSwitches = (baseline, pending) => Object.fromEntries(Object.keys(baseline)
|
|
9
|
+
.filter((name) => pending[name] !== baseline[name])
|
|
10
|
+
.map((name) => [name, pending[name]]));
|
|
11
|
+
// Splits the flipped switches into the enable/disable key lists of a role-wide update.
|
|
12
|
+
export const toRolePermissionChange = (switchKeys, changes) => {
|
|
13
|
+
const enablePermissions = [];
|
|
14
|
+
const disablePermissions = [];
|
|
15
|
+
Object.entries(changes).forEach(([name, enabled]) => {
|
|
16
|
+
(enabled ? enablePermissions : disablePermissions).push(...switchKeys[name]);
|
|
17
|
+
});
|
|
18
|
+
return { enablePermissions, disablePermissions };
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@droppii-org/chat-sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.54",
|
|
4
4
|
"description": "Droppii React Chat SDK",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"@ant-design/cssinjs": "^1.24.0",
|
|
87
87
|
"@ant-design/icons": "^6.0.0",
|
|
88
|
-
"@droppii-org/wasm-client-sdk": "0.
|
|
88
|
+
"@droppii-org/wasm-client-sdk": "0.7.0",
|
|
89
89
|
"@lexical/code": "^0.34.0",
|
|
90
90
|
"@lexical/html": "^0.34.0",
|
|
91
91
|
"@lexical/link": "^0.34.0",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface MemberPermissionOverrides {
|
|
2
|
-
sendMessage: boolean;
|
|
3
|
-
addMember: boolean;
|
|
4
|
-
pinMessage: boolean;
|
|
5
|
-
viewMemberInformation: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface AdminPermissionOverrides {
|
|
8
|
-
sendMessage: boolean;
|
|
9
|
-
pinMessage: boolean;
|
|
10
|
-
viewMemberInformation: boolean;
|
|
11
|
-
addMember: boolean;
|
|
12
|
-
removeMember: boolean;
|
|
13
|
-
updateGroupInfo: boolean;
|
|
14
|
-
}
|
|
15
|
-
interface GroupPermissionOverridesStore {
|
|
16
|
-
memberOverridesByGroup: Record<string, MemberPermissionOverrides>;
|
|
17
|
-
adminOverridesByGroup: Record<string, AdminPermissionOverrides>;
|
|
18
|
-
setMemberOverrides: (groupID: string, overrides: MemberPermissionOverrides) => void;
|
|
19
|
-
setAdminOverrides: (groupID: string, overrides: AdminPermissionOverrides) => void;
|
|
20
|
-
}
|
|
21
|
-
declare const useGroupPermissionOverridesStore: import("zustand").UseBoundStore<import("zustand").StoreApi<GroupPermissionOverridesStore>>;
|
|
22
|
-
export default useGroupPermissionOverridesStore;
|
|
23
|
-
//# sourceMappingURL=groupPermissionOverrides.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"groupPermissionOverrides.d.ts","sourceRoot":"","sources":["../../src/store/groupPermissionOverrides.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,qBAAqB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,6BAA6B;IACrC,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;IAClE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAChE,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACpF,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACnF;AAED,QAAA,MAAM,gCAAgC,4FAmBrC,CAAC;AAEF,eAAe,gCAAgC,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { create } from "zustand";
|
|
2
|
-
const useGroupPermissionOverridesStore = create((set) => ({
|
|
3
|
-
memberOverridesByGroup: {},
|
|
4
|
-
adminOverridesByGroup: {},
|
|
5
|
-
setMemberOverrides: (groupID, overrides) => set((state) => ({
|
|
6
|
-
memberOverridesByGroup: Object.assign(Object.assign({}, state.memberOverridesByGroup), { [groupID]: overrides }),
|
|
7
|
-
})),
|
|
8
|
-
setAdminOverrides: (groupID, overrides) => set((state) => ({
|
|
9
|
-
adminOverridesByGroup: Object.assign(Object.assign({}, state.adminOverridesByGroup), { [groupID]: overrides }),
|
|
10
|
-
})),
|
|
11
|
-
}));
|
|
12
|
-
export default useGroupPermissionOverridesStore;
|