feat: 增加群组配置权限,增加群组成员隐私控制选项

pull/64/head
moonrailgun 2 years ago
parent 99564c4fb1
commit 9b331e7707

@ -6,6 +6,13 @@ export enum GroupPanelType {
PLUGIN = 2,
}
export const groupConfigNames = [
// 隐藏群组成员标识位
'hideGroupMemberDiscriminator',
] as const;
export type GroupConfigNames = typeof groupConfigNames[number];
export interface GroupMember {
roles: string[]; // 角色组
userId: string;
@ -51,6 +58,7 @@ export interface GroupInfo {
members: GroupMember[];
panels: GroupPanel[];
roles: GroupRole[];
config?: Partial<Record<GroupConfigNames, any>>;
/**
*
*
@ -135,6 +143,24 @@ export async function modifyGroupField(
});
}
/**
*
* @param groupId ID
* @param configName
* @param configValue
*/
export async function modifyGroupConfig(
groupId: string,
configName: GroupConfigNames,
configValue: unknown
) {
await request.post('/api/group/updateGroupConfig', {
groupId,
configName,
configValue,
});
}
/**
* 退()
* socket退

@ -39,6 +39,7 @@ export const PERMISSION = {
invite: 'core.invite',
unlimitedInvite: 'core.unlimitedInvite',
groupDetail: 'core.groupDetail',
groupConfig: 'core.groupConfig',
manageUser: 'core.manageUser',
managePanel: 'core.managePanel',
manageInvite: 'core.manageInvite',
@ -72,6 +73,12 @@ export const getPermissionList = (): PermissionItemType[] => [
desc: t('允许成员查看群组详情'),
default: false,
},
{
key: PERMISSION.core.groupConfig,
title: t('修改群组配置'),
desc: t('允许成员修改群组配置'),
default: false,
},
{
key: PERMISSION.core.manageUser,
title: t('允许管理用户'),

@ -150,6 +150,10 @@ export const MembersPanel: React.FC<MembersPanelProps> = React.memo((props) => {
[groupId]
);
const config = groupInfo?.config ?? {};
const hideGroupMemberDiscriminator =
config.hideGroupMemberDiscriminator ?? false;
if (userInfoList.length === 0) {
return <Skeleton />;
}
@ -230,7 +234,13 @@ export const MembersPanel: React.FC<MembersPanelProps> = React.memo((props) => {
<div>
<UserListItem
userId={member._id}
popover={<GroupUserPopover userInfo={member} />}
popover={
<GroupUserPopover
userInfo={member}
hideDiscriminator={hideGroupMemberDiscriminator}
/>
}
hideDiscriminator={hideGroupMemberDiscriminator}
/>
</div>
</Dropdown>
@ -240,7 +250,13 @@ export const MembersPanel: React.FC<MembersPanelProps> = React.memo((props) => {
<UserListItem
key={member._id}
userId={member._id}
popover={<GroupUserPopover userInfo={member} />}
popover={
<GroupUserPopover
userInfo={member}
hideDiscriminator={hideGroupMemberDiscriminator}
/>
}
hideDiscriminator={hideGroupMemberDiscriminator}
/>
);
}

@ -9,9 +9,10 @@ interface UserListItemProps {
userId: string;
popover?: PopoverProps['content'];
actions?: React.ReactElement[];
hideDiscriminator?: boolean;
}
export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
const { actions = [] } = props;
const { actions = [], hideDiscriminator = false } = props;
const userInfo = useCachedUserInfo(props.userId);
const [isOnline] = useCachedOnlineStatus([props.userId]);
const userName = userInfo.nickname;
@ -38,9 +39,11 @@ export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
</div>
<div className="flex-1 text-gray-900 dark:text-white">
<span>{userName}</span>
<span className="text-gray-500 dark:text-gray-300 opacity-0 group-hover:opacity-100">
#{userInfo.discriminator}
</span>
{!hideDiscriminator && (
<span className="text-gray-500 dark:text-gray-300 opacity-0 group-hover:opacity-100">
#{userInfo.discriminator}
</span>
)}
</div>
<Space>{actions}</Space>
</Skeleton>

@ -0,0 +1,53 @@
import React from 'react';
import {
model,
showSuccessToasts,
t,
useAsyncRequest,
useGroupInfo,
} from 'tailchat-shared';
import { Loading } from '@/components/Loading';
import { FullModalField } from '@/components/FullModal/Field';
import { FullModalCommonTitle } from '@/components/FullModal/CommonTitle';
import { Switch } from 'antd';
export const GroupConfig: React.FC<{
groupId: string;
}> = React.memo((props) => {
const groupId = props.groupId;
const groupInfo = useGroupInfo(groupId);
const [{ loading }, handleModifyConfig] = useAsyncRequest(
async (configName: model.group.GroupConfigNames, configValue: any) => {
await model.group.modifyGroupConfig(groupId, configName, configValue);
},
[groupId]
);
if (!groupInfo) {
return <Loading spinning={true} />;
}
const config = groupInfo.config ?? {};
return (
<div>
<FullModalCommonTitle>{t('群组配置')}</FullModalCommonTitle>
<FullModalField
title={t('隐藏成员完整名称')}
tip={t('群组隐私控制,防止通过群组恶意获取成员信息')}
content={
<Switch
disabled={loading}
checked={config['hideGroupMemberDiscriminator'] ?? false}
onChange={(checked) =>
handleModifyConfig('hideGroupMemberDiscriminator', checked)
}
/>
}
/>
</div>
);
});
GroupConfig.displayName = 'GroupConfig';

@ -13,6 +13,7 @@ import { GroupPanel } from './Panel';
import { GroupRole } from './Role';
import { GroupSummary } from './Summary';
import _compact from 'lodash/compact';
import { GroupConfig } from './Config';
interface SettingsViewProps {
groupId: string;
@ -28,12 +29,17 @@ export const GroupDetail: React.FC<SettingsViewProps> = React.memo((props) => {
},
[props.onClose]
);
const [allowManagePanel, allowManageInvite, allowManageRoles] =
useHasGroupPermission(groupId, [
PERMISSION.core.managePanel,
PERMISSION.core.manageInvite,
PERMISSION.core.manageRoles,
]);
const [
allowManageConfig,
allowManagePanel,
allowManageInvite,
allowManageRoles,
] = useHasGroupPermission(groupId, [
PERMISSION.core.groupConfig,
PERMISSION.core.managePanel,
PERMISSION.core.manageInvite,
PERMISSION.core.manageRoles,
]);
const menu: SidebarViewMenuType[] = useMemo(() => {
// 内置
@ -47,6 +53,11 @@ export const GroupDetail: React.FC<SettingsViewProps> = React.memo((props) => {
title: t('概述'),
content: <GroupSummary groupId={groupId} />,
},
allowManageConfig && {
type: 'item',
title: t('配置'),
content: <GroupConfig groupId={groupId} />,
},
allowManagePanel && {
type: 'item',
title: t('面板'),

@ -7,8 +7,9 @@ import { UserProfileContainer } from '../UserProfileContainer';
export const GroupUserPopover: React.FC<{
userInfo: UserBaseInfo;
hideDiscriminator?: boolean;
}> = React.memo((props) => {
const { userInfo } = props;
const { userInfo, hideDiscriminator = false } = props;
const userExtra = userInfo.extra ?? {};
useEffect(() => {
@ -24,7 +25,9 @@ export const GroupUserPopover: React.FC<{
<UserProfileContainer userInfo={userInfo}>
<div className="text-xl">
<span className="font-semibold">{userInfo.nickname}</span>
<span className="opacity-60 ml-1">#{userInfo.discriminator}</span>
{!hideDiscriminator && (
<span className="opacity-60 ml-1">#{userInfo.discriminator}</span>
)}
</div>
<div>

@ -138,6 +138,12 @@ export class Group extends TimeStamps implements Base {
})
fallbackPermissions: string[];
/**
*
*/
@prop({ default: () => ({}) })
config: object;
/**
*
*/

@ -8,6 +8,7 @@ export const PERMISSION = {
invite: 'core.invite',
unlimitedInvite: 'core.unlimitedInvite',
groupDetail: 'core.groupDetail',
groupConfig: 'core.groupConfig',
manageUser: 'core.manageUser',
managePanel: 'core.managePanel',
manageInvite: 'core.manageInvite',

@ -66,6 +66,13 @@ class GroupService extends TcService {
fieldValue: 'any',
},
});
this.registerAction('updateGroupConfig', this.updateGroupConfig, {
params: {
groupId: 'string',
configName: 'string',
configValue: 'any',
},
});
this.registerAction('isGroupOwner', this.isGroupOwner, {
params: {
groupId: 'string',
@ -353,6 +360,37 @@ class GroupService extends TcService {
this.notifyGroupInfoUpdate(ctx, group);
}
/**
*
*/
async updateGroupConfig(
ctx: TcContext<{
groupId: string;
configName: string;
configValue: unknown;
}>
) {
const { groupId, configName, configValue } = ctx.params;
const userId = ctx.meta.userId;
const t = ctx.meta.t;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.groupConfig]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
const group = await this.adapter.model.findById(groupId).exec();
group.config[configName] = configValue;
await group.save();
this.notifyGroupInfoUpdate(ctx, group);
}
/**
*
*/

Loading…
Cancel
Save