@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.
Files changed (35) hide show
  1. package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
  2. package/dist/components/conversation-detail/ConversationMessageList.js +8 -0
  3. package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
  4. package/dist/components/group/AdminPermissionModal.js +9 -38
  5. package/dist/components/group/MemberPermissionPanel.d.ts.map +1 -1
  6. package/dist/components/group/MemberPermissionPanel.js +45 -33
  7. package/dist/components/message/MessageList.d.ts.map +1 -1
  8. package/dist/components/message/MessageList.js +6 -0
  9. package/dist/constants/index.d.ts +1 -0
  10. package/dist/constants/index.d.ts.map +1 -1
  11. package/dist/constants/index.js +2 -0
  12. package/dist/hooks/conversation/useViewOnlyGroupRefresh.d.ts +14 -0
  13. package/dist/hooks/conversation/useViewOnlyGroupRefresh.d.ts.map +1 -0
  14. package/dist/hooks/conversation/useViewOnlyGroupRefresh.js +44 -0
  15. package/dist/hooks/group/useGroupMemberActions.d.ts +7 -0
  16. package/dist/hooks/group/useGroupMemberActions.d.ts.map +1 -1
  17. package/dist/hooks/group/useGroupMemberActions.js +23 -0
  18. package/dist/hooks/message/useMessage.d.ts +2 -0
  19. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  20. package/dist/hooks/message/useMessage.js +84 -0
  21. package/dist/hooks/search/useSearchGroups.d.ts +1 -0
  22. package/dist/hooks/search/useSearchGroups.d.ts.map +1 -1
  23. package/dist/hooks/search/useSearchGroups.js +11 -1
  24. package/dist/locales/vi/common.json +1 -0
  25. package/dist/store/conversation.d.ts.map +1 -1
  26. package/dist/store/conversation.js +73 -0
  27. package/dist/store/type.d.ts +1 -0
  28. package/dist/store/type.d.ts.map +1 -1
  29. package/dist/utils/groupPermissionSwitches.d.ts +9 -0
  30. package/dist/utils/groupPermissionSwitches.d.ts.map +1 -0
  31. package/dist/utils/groupPermissionSwitches.js +19 -0
  32. package/package.json +2 -2
  33. package/dist/store/groupPermissionOverrides.d.ts +0 -23
  34. package/dist/store/groupPermissionOverrides.d.ts.map +0 -1
  35. 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;AAwBzE,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,4CAsQ9B,CAAC;AAEF,eAAe,uBAAuB,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":"AAaA,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;AA6DD,QAAA,MAAM,oBAAoB,GAAI,uEAO3B,yBAAyB,mDA2K3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
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 at once: switches start from what the Admins already share, and "Lưu"
51
- // writes only the switches the user flipped, so each Admin keeps the rest.
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 = Object.fromEntries(SWITCHES.filter((name) => pending[name] !== baseline[name]).map((name) => [
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 Promise.all(admins.map((admin) => {
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":"AAYA,UAAU,0BAA0B;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAMD,QAAA,MAAM,qBAAqB,GAAI,4BAA4B,0BAA0B,mDAqHpF,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
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 { useGroupPermission } from "../../hooks/group/useGroupPermission";
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 useGroupPermissionOverridesStore from "../../store/groupPermissionOverrides";
10
- // Mocked config UI (DROPPII-30248 follow-up, "Configure Member Permissions")
11
- // — renders inline within the "Thông tin" sidebar (back arrow, no gray
12
- // background) per the reference recording, not as a Modal popup. No BE
13
- // persistence yet, committed to the in-memory override store on "Lưu" only.
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 permissions = useGroupPermission(groupID);
17
- const setMemberOverrides = useGroupPermissionOverridesStore((state) => state.setMemberOverrides);
31
+ const { members: groupMembers, isLoading } = useGroupMembers(open ? groupID : undefined);
32
+ const { mutateAsync: setMemberRolePermissions } = useSetMemberRolePermissions();
18
33
  const [isSaving, setIsSaving] = useState(false);
19
- const [pending, setPending] = useState({
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 (!open)
27
- return;
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 hasChanges = pending.sendMessage !== permissions.sendMessage ||
37
- pending.addMember !== permissions.addMember ||
38
- pending.pinMessage !== permissions.pinMessage ||
39
- pending.viewMemberInformation !== permissions.viewMemberInformation;
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
- setMemberOverrides(groupID, pending);
45
- message.success(t("update_member_permissions_success"));
46
- setIsSaving(false);
47
- onClose();
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: _jsxs("div", { className: "flex flex-col py-2", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_send_message") }), _jsx(Switch, { checked: pending.sendMessage, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { sendMessage: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_add_member") }), _jsx(Switch, { checked: pending.addMember, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { addMember: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_pin_message") }), _jsx(Switch, { checked: pending.pinMessage, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { pinMessage: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_view_member_info") }), _jsx(Switch, { checked: pending.viewMemberInformation, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { viewMemberInformation: checked }))) })] })] }) }) }), _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, onClick: handleSave, children: t("save") })] })] }));
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":"AAoCA,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,4CAsZ3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
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;AAEX,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAE3D,eAAO,MAAM,4BAA4B,2BAA2B,CAAC;AAMrE,eAAO,MAAM,sBAAsB,4BAA4B,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"}
@@ -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;;;;;;CAoVnC,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"}
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;AAElD,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;AA4BD,eAAO,MAAM,eAAe,GAAI,2CAK7B,qBAAqB;;;;;;CAkEvB,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 : buildFallbackConversation(group),
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":"AAWA,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,gFAmcvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
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,
@@ -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;
@@ -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.52",
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.6.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;