From 25beff55262aba3cf7b93520deeaa6c726a12e8d Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Thu, 16 Dec 2021 20:32:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BB=85=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E6=8C=87=E5=AE=9A=E7=94=A8=E6=88=B7=E5=8F=91=E8=A8=80?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/model/useUserInfoList.ts | 16 +++++++ shared/hooks/model/useUsernames.ts | 11 ++--- shared/i18n/langs/en-US/translation.json | 4 ++ shared/i18n/langs/zh-CN/translation.json | 4 ++ shared/index.tsx | 1 + shared/redux/hooks/useGroup.ts | 14 ++++++ web/src/components/UserSelector.tsx | 38 +++++++++++++++ .../components/modals/CreateGroupPanel.tsx | 48 ++++++++++++++++++- 8 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 shared/hooks/model/useUserInfoList.ts create mode 100644 web/src/components/UserSelector.tsx diff --git a/shared/hooks/model/useUserInfoList.ts b/shared/hooks/model/useUserInfoList.ts new file mode 100644 index 00000000..bc2e8a8b --- /dev/null +++ b/shared/hooks/model/useUserInfoList.ts @@ -0,0 +1,16 @@ +import { getCachedUserInfo } from '../../cache/cache'; +import type { UserBaseInfo } from '../../model/user'; +import { useAsync } from '../useAsync'; + +/** + * 用户信息列表 + */ +export function useUserInfoList(userIds: string[] = []): UserBaseInfo[] { + const { value: userInfoList = [] } = useAsync(async () => { + const users = await Promise.all(userIds.map((id) => getCachedUserInfo(id))); + + return users; + }, [userIds.join(',')]); + + return userInfoList; +} diff --git a/shared/hooks/model/useUsernames.ts b/shared/hooks/model/useUsernames.ts index 815d5778..1df348bb 100644 --- a/shared/hooks/model/useUsernames.ts +++ b/shared/hooks/model/useUsernames.ts @@ -1,15 +1,10 @@ -import { getCachedUserInfo } from '../../cache/cache'; -import { useAsync } from '../useAsync'; +import { useUserInfoList } from './useUserInfoList'; /** * 用户名列表 */ export function useUsernames(userIds: string[]): string[] { - const { value: names = [] } = useAsync(async () => { - const users = await Promise.all(userIds.map((id) => getCachedUserInfo(id))); + const userInfoList = useUserInfoList(userIds); - return users.map((user) => user.nickname); - }, [userIds.join(',')]); - - return names; + return userInfoList.map((info) => info.nickname); } diff --git a/shared/i18n/langs/en-US/translation.json b/shared/i18n/langs/en-US/translation.json index c6e04104..75b78921 100644 --- a/shared/i18n/langs/en-US/translation.json +++ b/shared/i18n/langs/en-US/translation.json @@ -2,6 +2,7 @@ "k10c018fe": "Teamwork", "k1141d649": "Version update", "k131598d0": "A new version is detected, whether to refresh immediately to upgrade to the latest content", + "k1596c75c": "Only allow specified users to speak", "k162e37f1": "Plugin is successfully uninstalled, and it needs to be restarted to take effect", "k1704ea49": "Install", "k18580d81": "Create a link and send it to external friends", @@ -54,11 +55,13 @@ "k517db7e5": "Text Channel", "k51db56bf": "Temporary Meeting", "k551b0348": "Password", + "k57ab4d97": "Please select user", "k58a85592": "Is not a valid plugin configuration", "k5bb71ad7": "Installed", "k5bec387": "Unable to get group information", "k5f91e72c": "Built Plugins", "k61a1db2": "Already applied", + "k62051fcc": "Upload failed", "k620a58f8": "Guest Login", "k621f7e70": "Quick search, jump", "k62f009e7": "Modify group name success", @@ -178,6 +181,7 @@ "kf7d6acd4": "Create Guest", "kf7d829eb": "Wait to process", "kf87f3059": "Plugin Store", + "kf98fbe5e": "Forbid everyone to speak", "kfa01c850": "No private message found", "kfa493f3f": "Reply", "kfaddd61d": "Chat Service", diff --git a/shared/i18n/langs/zh-CN/translation.json b/shared/i18n/langs/zh-CN/translation.json index 92bdb342..6f497dd6 100644 --- a/shared/i18n/langs/zh-CN/translation.json +++ b/shared/i18n/langs/zh-CN/translation.json @@ -2,6 +2,7 @@ "k10c018fe": "工作协同", "k1141d649": "更新版本", "k131598d0": "检测到有新版本, 是否立即刷新以升级到最新内容", + "k1596c75c": "仅允许指定用户发言", "k162e37f1": "插件卸载成功, 需要重启后生效", "k1704ea49": "安装", "k18580d81": "创建链接并发送给外部好友", @@ -54,11 +55,13 @@ "k517db7e5": "文字频道", "k51db56bf": "临时会议", "k551b0348": "密码", + "k57ab4d97": "请选择用户", "k58a85592": "不是一个合法的插件配置", "k5bb71ad7": "已安装", "k5bec387": "无法获取到群组信息", "k5f91e72c": "内置插件", "k61a1db2": "已申请", + "k62051fcc": "上传失败", "k620a58f8": "游客访问", "k621f7e70": "快速搜索、跳转", "k62f009e7": "修改群组名成功", @@ -178,6 +181,7 @@ "kf7d6acd4": "创建访客", "kf7d829eb": "待处理", "kf87f3059": "插件中心", + "kf98fbe5e": "禁止所有人发言", "kfa01c850": "找不到私信会话", "kfa493f3f": "回复", "kfaddd61d": "聊天服务", diff --git a/shared/index.tsx b/shared/index.tsx index 31e82c0a..b2b7391a 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -134,6 +134,7 @@ export { useConverseMessage } from './redux/hooks/useConverseMessage'; export { useDMConverseName } from './redux/hooks/useDMConverseName'; export { useGroupInfo, + useGroupMemberUUIDs, useGroupPanel, useIsGroupOwner, useGroupUnread, diff --git a/shared/redux/hooks/useGroup.ts b/shared/redux/hooks/useGroup.ts index b64fde2a..e2c4388b 100644 --- a/shared/redux/hooks/useGroup.ts +++ b/shared/redux/hooks/useGroup.ts @@ -12,6 +12,20 @@ export function useGroupInfo(groupId: string): GroupInfo | null { return useAppSelector((state) => state.group.groups[groupId]) ?? null; } +/** + * 获取群组中所有成员的uuid列表 + */ +export function useGroupMemberUUIDs(groupId: string): string[] { + const groupInfo = useGroupInfo(groupId); + const members = groupInfo?.members ?? []; + const groupMemberUUIDs = useMemo( + () => members.map((m) => m.userId), + [members] + ); + + return groupMemberUUIDs; +} + /** * 获取群组面板信息 */ diff --git a/web/src/components/UserSelector.tsx b/web/src/components/UserSelector.tsx new file mode 100644 index 00000000..39b4ccae --- /dev/null +++ b/web/src/components/UserSelector.tsx @@ -0,0 +1,38 @@ +import { Select } from 'antd'; +import React, { useCallback } from 'react'; +import { t } from 'tailchat-shared'; +import { useUserInfoList } from 'tailchat-shared/hooks/model/useUserInfoList'; + +interface UserSelectorProps { + allUserIds: string[]; + userIds?: string[]; + onChange?: (userIds: string[]) => void; +} +export const UserSelector: React.FC = React.memo((props) => { + const { allUserIds, userIds, onChange } = props; + const userInfoList = useUserInfoList(allUserIds); + + const handleChange = useCallback( + (userIds: string[]) => { + typeof onChange === 'function' && onChange(userIds); + }, + [onChange] + ); + + return ( + + ); +}); +UserSelector.displayName = 'UserSelector'; diff --git a/web/src/components/modals/CreateGroupPanel.tsx b/web/src/components/modals/CreateGroupPanel.tsx index 87ec17ec..68df4fbb 100644 --- a/web/src/components/modals/CreateGroupPanel.tsx +++ b/web/src/components/modals/CreateGroupPanel.tsx @@ -10,13 +10,18 @@ import { createFastFormSchema, fieldSchema, showToasts, + useGroupMemberUUIDs, + isDevelopment, } from 'tailchat-shared'; import { ModalWrapper } from '../Modal'; import { WebFastForm } from '../WebFastForm'; +import _compact from 'lodash/compact'; +import { UserSelector } from '../UserSelector'; +import { useFastFormContext } from 'tailchat-shared/components/FastForm/context'; interface Values { name: string; - type: string; + type: string | GroupPanelType; [key: string]: unknown; } @@ -93,7 +98,48 @@ export const ModalCreateGroupPanel: React.FC<{ [props.groupId, props.onCreateSuccess] ); + const disableSendMessageWithoutRender = useMemo(() => { + const DisableSendMessageWithoutComponent: React.FC = () => { + const groupMemberUUIDs = useGroupMemberUUIDs(props.groupId); + const context = useFastFormContext(); + + return ( + { + context?.setValues({ + ...context.values, + disableSendMessageWithout: userIds, + }); + }} + /> + ); + }; + DisableSendMessageWithoutComponent.displayName = + 'DisableSendMessageWithoutComponent'; + + return DisableSendMessageWithoutComponent; + }, [props.groupId]); + const field = useMemo(() => { + // NOTICE: 仅开发环境有这个配置 + if (isDevelopment && currentValues.type === GroupPanelType.TEXT) { + return _compact([ + ...baseFields, + { + type: 'checkbox', + name: 'disableSendMessage', + label: t('禁止所有人发言'), + }, + currentValues.disableSendMessage === true && { + type: 'custom', + name: 'disableSendMessageWithout', + label: t('仅允许指定用户发言'), + render: disableSendMessageWithoutRender, + }, + ]) as FastFormFieldMeta[]; + } + if (typeof currentValues.type === 'string') { // 如果当前选择的面板类型为插件类型 // 需要从插件信息中获取额外的字段