From 8e594008bea03bd74fd4c90e0c42b4dd92031b7f Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Thu, 23 Jun 2022 17:18:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8A=BD=E8=B1=A1=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=B8=8E=E5=A2=9E=E5=8A=A0=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E7=BB=84=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/useSearch.ts | 23 +++++++++++++ shared/i18n/langs/en-US/translation.json | 1 + shared/i18n/langs/zh-CN/translation.json | 1 + shared/index.tsx | 1 + .../components/Panel/group/MembersPanel.tsx | 23 ++++++++----- .../modals/GroupDetail/Role/index.tsx | 33 +++++++++++++++---- 6 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 shared/hooks/useSearch.ts diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts new file mode 100644 index 00000000..e6c63e41 --- /dev/null +++ b/shared/hooks/useSearch.ts @@ -0,0 +1,23 @@ +import { useMemo, useState } from 'react'; + +export interface UseSearchOptions { + dataSource: T[]; + filterFn: (item: T, searchText: string) => boolean; +} + +export function useSearch(options: UseSearchOptions) { + const { dataSource, filterFn } = options; + const [searchText, setSearchText] = useState(''); + const isSearching = searchText !== ''; + + const searchResult = useMemo(() => { + return dataSource.filter((item) => filterFn(item, searchText)); + }, [dataSource, searchText]); + + return { + searchText, + setSearchText, + isSearching, + searchResult, + }; +} diff --git a/shared/i18n/langs/en-US/translation.json b/shared/i18n/langs/en-US/translation.json index 63fdabbd..2dd12fdd 100644 --- a/shared/i18n/langs/en-US/translation.json +++ b/shared/i18n/langs/en-US/translation.json @@ -234,6 +234,7 @@ "ke17b2c87": "Do not upload pictures that violate local laws and regulations", "ke187440d": "Panel type cannot be empty", "kea977d95": "The following users are offline", + "kec46a57f": "Add members", "kecb51e2c": "Old password", "kecbd7449": "Delete", "ked2baf28": "Loading...", diff --git a/shared/i18n/langs/zh-CN/translation.json b/shared/i18n/langs/zh-CN/translation.json index 5bd0d0ec..6e74b282 100644 --- a/shared/i18n/langs/zh-CN/translation.json +++ b/shared/i18n/langs/zh-CN/translation.json @@ -234,6 +234,7 @@ "ke17b2c87": "请勿上传违反当地法律法规的图片", "ke187440d": "面板类型不能为空", "kea977d95": "以下用户已离线", + "kec46a57f": "添加成员", "kecb51e2c": "旧密码", "kecbd7449": "删除", "ked2baf28": "加载中...", diff --git a/shared/index.tsx b/shared/index.tsx index 3ce4f481..f7dae6f7 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -63,6 +63,7 @@ export { useMemoizedFn } from './hooks/useMemoizedFn'; export { useMountedState } from './hooks/useMountedState'; export { usePrevious } from './hooks/usePrevious'; export { useRafState } from './hooks/useRafState'; +export { useSearch } from './hooks/useSearch'; export { useUpdateRef } from './hooks/useUpdateRef'; export { useWhyDidYouUpdate } from './hooks/useWhyDidYouUpdate'; diff --git a/web/src/components/Panel/group/MembersPanel.tsx b/web/src/components/Panel/group/MembersPanel.tsx index 04c6551f..9d402451 100644 --- a/web/src/components/Panel/group/MembersPanel.tsx +++ b/web/src/components/Panel/group/MembersPanel.tsx @@ -2,12 +2,13 @@ import { Icon } from '@/components/Icon'; import { GroupUserPopover } from '@/components/popover/GroupUserPopover'; import { UserListItem } from '@/components/UserListItem'; import { Divider, Input, Skeleton } from 'antd'; -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { t, useCachedOnlineStatus, useGroupInfo, UserBaseInfo, + useSearch, } from 'tailchat-shared'; import { useUserInfoList } from 'tailchat-shared/hooks/model/useUserInfoList'; @@ -22,11 +23,20 @@ export const MembersPanel: React.FC = React.memo((props) => { const groupInfo = useGroupInfo(props.groupId); const members = groupInfo?.members ?? []; const userInfoList = useUserInfoList(members.map((m) => m.userId)); - const [searchStr, setSearchStr] = useState(''); const membersOnlineStatus = useCachedOnlineStatus( members.map((m) => m.userId) ); + const { + searchText, + setSearchText, + isSearching, + searchResult: filteredGroupMembers, + } = useSearch({ + dataSource: userInfoList, + filterFn: (item, searchText) => item.nickname.includes(searchText), + }); + const groupedMembers = useMemo(() => { const online: UserBaseInfo[] = []; const offline: UserBaseInfo[] = []; @@ -45,16 +55,10 @@ export const MembersPanel: React.FC = React.memo((props) => { }; }, [userInfoList, membersOnlineStatus]); - const filteredGroupMembers = useMemo(() => { - return userInfoList.filter((u) => u.nickname.includes(searchStr)); - }, [userInfoList, searchStr]); - if (userInfoList.length === 0) { return ; } - const isSearching = searchStr !== ''; - const renderUser = (member: UserBaseInfo) => ( = React.memo((props) => { placeholder={t('搜索成员')} size="large" suffix={} - onChange={(e) => setSearchStr(e.target.value)} + value={searchText} + onChange={(e) => setSearchText(e.target.value)} /> diff --git a/web/src/components/modals/GroupDetail/Role/index.tsx b/web/src/components/modals/GroupDetail/Role/index.tsx index 6f3f24e0..bfb68708 100644 --- a/web/src/components/modals/GroupDetail/Role/index.tsx +++ b/web/src/components/modals/GroupDetail/Role/index.tsx @@ -1,9 +1,10 @@ import { IconBtn } from '@/components/IconBtn'; import { PillTabPane, PillTabs } from '@/components/PillTabs'; import { UserListItem } from '@/components/UserListItem'; -import { Button } from 'antd'; +import { Button, Input } from 'antd'; import React, { useCallback } from 'react'; -import { t, useGroupInfo } from 'tailchat-shared'; +import { Icon } from 'tailchat-design'; +import { t, useGroupInfo, useSearch, useUserInfoList } from 'tailchat-shared'; import { PermissionItem } from './PermissionItem'; import { RoleItem } from './RoleItem'; @@ -14,6 +15,16 @@ export const GroupRole: React.FC = React.memo((props) => { const { groupId } = props; const groupInfo = useGroupInfo(groupId); const members = groupInfo?.members ?? []; + const userInfoList = useUserInfoList(members.map((m) => m.userId)); + const { + searchText, + setSearchText, + isSearching, + searchResult: filterMembers, + } = useSearch({ + dataSource: userInfoList, + filterFn: (item, searchText) => item.nickname.includes(searchText), + }); const handleAddMember = useCallback(() => {}, []); @@ -37,16 +48,24 @@ export const GroupRole: React.FC = React.memo((props) => { {/* 管理成员 */} -
+
+ } + value={searchText} + onChange={(e) => setSearchText(e.target.value)} + /> +
- {members.map((m) => ( + {(isSearching ? filterMembers : userInfoList).map((m) => ( ]} /> ))}