From ed3e9ca6d756fda56af6e4e3a18f54dee6786edf Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Sat, 11 Nov 2023 01:23:28 +0800 Subject: [PATCH] feat: add fast change role group with group member right click menu manage #171 --- .../shared/i18n/langs/en-US/translation.json | 5 +- .../shared/i18n/langs/zh-CN/translation.json | 5 +- .../components/Panel/group/MembersPanel.tsx | 8 +- client/web/src/hooks/useGroupMemberAction.ts | 127 +++++++++++++----- 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/client/shared/i18n/langs/en-US/translation.json b/client/shared/i18n/langs/en-US/translation.json index bce844a5..9c7d630f 100644 --- a/client/shared/i18n/langs/en-US/translation.json +++ b/client/shared/i18n/langs/en-US/translation.json @@ -138,6 +138,7 @@ "k5d2a6631": "Allow to manage channels", "k5f91e72c": "Built Plugins", "k5fc9ccb6": "Operation too frequently", + "k5fde4f8e": "Assign roles", "k609d9f28": "Please enter the server address (example: http://127.0.0.1:11000)", "k61a1db2": "Already applied", "k62051fcc": "Upload failed", @@ -165,7 +166,7 @@ "k6cc401b0": "No role groups are currently selected", "k6cd79ab": "Click for details", "k6e4e3d7a": "All messages in this group are marked as read", - "k6eac768d": "Add Members", + "k6eac768d": "Add Roles", "k6ee71a71": "Global Configuration", "k6efe275c": "Mark as read", "k6fb230da": "Pending friend request", @@ -225,6 +226,7 @@ "k8f6ab535": "Unable to install", "k8f6dfd40": "Current members", "k9179206d": "Reconnecting", + "k924278f0": "Remove user [{{name}}] role [{{roleName}}] success", "k9277af78": "Retry", "k92a84117": "Claim account", "k95222176": "Unread", @@ -328,6 +330,7 @@ "kc74e5f62": "Search for members", "kc77e00c7": "Allow members to view the panel", "kc7cc96c8": "From", + "kc8c4ccbb": "Grant user [{{name}}] role [{{roleName}}] success", "kc9283683": "Are you sure you want to delete the panel [{{name}}]", "kc9bd3ad6": "Reset to default permissions", "kc9cc6154": "Friends", diff --git a/client/shared/i18n/langs/zh-CN/translation.json b/client/shared/i18n/langs/zh-CN/translation.json index 1897dad4..89740031 100644 --- a/client/shared/i18n/langs/zh-CN/translation.json +++ b/client/shared/i18n/langs/zh-CN/translation.json @@ -138,6 +138,7 @@ "k5d2a6631": "允许管理频道", "k5f91e72c": "内置插件", "k5fc9ccb6": "操作过于频繁", + "k5fde4f8e": "分配身份组", "k609d9f28": "请输入服务器地址(示例: http://127.0.0.1:11000)", "k61a1db2": "已申请", "k62051fcc": "上传失败", @@ -165,7 +166,7 @@ "k6cc401b0": "当前没有选择任何角色组", "k6cd79ab": "点击查看详情", "k6e4e3d7a": "已标记该群组所有消息已读", - "k6eac768d": "添加角色", + "k6eac768d": "添加身份组", "k6ee71a71": "全局配置", "k6efe275c": "标记为已读", "k6fb230da": "等待处理的好友请求", @@ -225,6 +226,7 @@ "k8f6ab535": "无法安装", "k8f6dfd40": "当前成员数", "k9179206d": "正在重新链接", + "k924278f0": "移除用户 [{{name}}] 身份组 [{{roleName}}] 成功", "k9277af78": "重试", "k92a84117": "认领账号", "k95222176": "未读", @@ -328,6 +330,7 @@ "kc74e5f62": "搜索成员", "kc77e00c7": "允许成员查看面板", "kc7cc96c8": "来自", + "kc8c4ccbb": "授予用户 [{{name}}] 身份组 [{{roleName}}] 成功", "kc9283683": "确定要删除面板 【{{name}}】 么", "kc9bd3ad6": "重置为默认权限", "kc9cc6154": "好友", diff --git a/client/web/src/components/Panel/group/MembersPanel.tsx b/client/web/src/components/Panel/group/MembersPanel.tsx index 607e3c95..71da0042 100644 --- a/client/web/src/components/Panel/group/MembersPanel.tsx +++ b/client/web/src/components/Panel/group/MembersPanel.tsx @@ -33,9 +33,6 @@ export const MembersPanel: React.FC = React.memo((props) => { const membersOnlineStatus = useCachedOnlineStatus( members.map((m) => m.userId) ); - const [allowManageUser] = useHasGroupPermission(groupId, [ - PERMISSION.core.manageUser, - ]); const { hideGroupMemberDiscriminator } = getGroupConfigWithInfo(groupInfo); const { @@ -102,9 +99,8 @@ export const MembersPanel: React.FC = React.memo((props) => { return
; } - if (allowManageUser) { - const menu: MenuProps = generateActionMenu(member); - + const menu = generateActionMenu(member); + if ((menu.items ?? []).length > 0) { return (
diff --git a/client/web/src/hooks/useGroupMemberAction.ts b/client/web/src/hooks/useGroupMemberAction.ts index 62e2b807..f92953f0 100644 --- a/client/web/src/hooks/useGroupMemberAction.ts +++ b/client/web/src/hooks/useGroupMemberAction.ts @@ -1,16 +1,17 @@ import { openReconfirmModalP } from '@/components/Modal'; import type { MenuProps } from 'antd'; -import { useCallback } from 'react'; import { formatFullTime, humanizeMsDuration, model, + PERMISSION, showSuccessToasts, t, useAsyncRequest, + useEvent, useGroupInfo, useGroupMemberInfos, - useMemoizedFn, + useHasGroupPermission, UserBaseInfo, useSearch, } from 'tailchat-shared'; @@ -23,8 +24,13 @@ import { useFriendNicknameMap } from 'tailchat-shared/redux/hooks/useFriendNickn export function useGroupMemberAction(groupId: string) { const groupInfo = useGroupInfo(groupId); const members = groupInfo?.members ?? []; + const roles = groupInfo?.roles ?? []; const userInfos = useGroupMemberInfos(groupId); const friendNicknameMap = useFriendNicknameMap(); + const [allowManageUser, allowManageRoles] = useHasGroupPermission(groupId, [ + PERMISSION.core.manageUser, + PERMISSION.core.manageRoles, + ]); const { handleMuteMember, handleUnmuteMember } = useMemberMuteAction( groupId, @@ -64,26 +70,31 @@ export function useGroupMemberAction(groupId: string) { [groupId] ); - const getMemberHasMute = useCallback( - (userId: string): boolean => { - const member = members.find((m) => m.userId === userId); + const getMemberHasMute = useEvent((userId: string): boolean => { + const member = members.find((m) => m.userId === userId); - if (!member || !member.muteUntil) { - return false; - } + if (!member || !member.muteUntil) { + return false; + } - const muteUntil = member.muteUntil; + const muteUntil = member.muteUntil; - return new Date(muteUntil).valueOf() > new Date().valueOf(); - }, - [members] - ); + return new Date(muteUntil).valueOf() > new Date().valueOf(); + }); - const generateActionMenu = useMemoizedFn( - (member: UserBaseInfo): MenuProps => { - const hasMute = getMemberHasMute(member._id); + const getMemberRoles = useEvent((userId: string): string[] => { + return members.find((m) => m.userId === userId)?.roles ?? []; + }); - const muteItems: MenuProps['items'] = hasMute + /** + * 生成群组成员操作菜单 + */ + const generateActionMenu = useEvent((member: UserBaseInfo): MenuProps => { + const hasMute = getMemberHasMute(member._id); + const memberRoles = getMemberRoles(member._id); + + const muteItems: MenuProps['items'] = allowManageUser + ? hasMute ? [ { key: 'unmute', @@ -136,23 +147,71 @@ export function useGroupMemberAction(groupId: string) { }, ], }, - ]; - - const menu: MenuProps = { - items: _compact([ - ...muteItems, - { - key: 'delete', - label: t('移出群组'), - danger: true, - onClick: () => handleRemoveGroupMember(member._id), - }, - ] as MenuProps['items']), - }; - - return menu; - } - ); + ] + : []; + + const roleItems: MenuProps['items'] = + allowManageRoles && roles.length > 0 + ? [ + { + key: 'manageRole', + label: t('分配身份组'), + children: roles.map((role) => ({ + key: role._id, + label: role.name, + className: memberRoles.includes(role._id) + ? 'underline' + : undefined, + onClick: async () => { + // switch member role + if (memberRoles.includes(role._id)) { + // 已拥有该身份 + await model.group.removeGroupMemberRoles( + groupId, + [member._id], + [role._id] + ); + showSuccessToasts( + t('移除用户 [{{name}}] 身份组 [{{roleName}}] 成功', { + name: member.nickname, + roleName: role.name, + }) + ); + } else { + // 没有该身份 + await model.group.appendGroupMemberRoles( + groupId, + [member._id], + [role._id] + ); + showSuccessToasts( + t('授予用户 [{{name}}] 身份组 [{{roleName}}] 成功', { + name: member.nickname, + roleName: role.name, + }) + ); + } + }, + })), + }, + ] + : []; + + const menu: MenuProps = { + items: _compact([ + ...muteItems, + ...roleItems, + allowManageUser && { + key: 'delete', + label: t('移出群组'), + danger: true, + onClick: () => handleRemoveGroupMember(member._id), + }, + ] as MenuProps['items']), + }; + + return menu; + }); return { userInfos,