feat: 抽象搜索逻辑与增加身份组搜索

pull/49/head
moonrailgun 3 years ago
parent 5996a7024a
commit 8e594008be

@ -0,0 +1,23 @@
import { useMemo, useState } from 'react';
export interface UseSearchOptions<T> {
dataSource: T[];
filterFn: (item: T, searchText: string) => boolean;
}
export function useSearch<T>(options: UseSearchOptions<T>) {
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,
};
}

@ -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...",

@ -234,6 +234,7 @@
"ke17b2c87": "请勿上传违反当地法律法规的图片",
"ke187440d": "面板类型不能为空",
"kea977d95": "以下用户已离线",
"kec46a57f": "添加成员",
"kecb51e2c": "旧密码",
"kecbd7449": "删除",
"ked2baf28": "加载中...",

@ -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';

@ -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<MembersPanelProps> = 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<MembersPanelProps> = React.memo((props) => {
};
}, [userInfoList, membersOnlineStatus]);
const filteredGroupMembers = useMemo(() => {
return userInfoList.filter((u) => u.nickname.includes(searchStr));
}, [userInfoList, searchStr]);
if (userInfoList.length === 0) {
return <Skeleton />;
}
const isSearching = searchStr !== '';
const renderUser = (member: UserBaseInfo) => (
<UserListItem
key={member._id}
@ -70,7 +74,8 @@ export const MembersPanel: React.FC<MembersPanelProps> = React.memo((props) => {
placeholder={t('搜索成员')}
size="large"
suffix={<Icon fontSize={20} color="grey" icon="mdi:magnify" />}
onChange={(e) => setSearchStr(e.target.value)}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
/>
</div>

@ -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<GroupPermissionProps> = 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<GroupPermissionProps> = React.memo((props) => {
</PillTabPane>
<PillTabPane key="member" tab="管理成员">
{/* 管理成员 */}
<div className="text-right mb-2">
<div className="text-right mb-2 flex space-x-1">
<Input
placeholder={t('搜索成员')}
size="middle"
suffix={<Icon fontSize={20} color="grey" icon="mdi:magnify" />}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
/>
<Button type="primary" onClick={handleAddMember}>
{t('添加成员')}
</Button>
</div>
{members.map((m) => (
{(isSearching ? filterMembers : userInfoList).map((m) => (
<UserListItem
key={m.userId}
userId={m.userId}
key={m._id}
userId={m._id}
actions={[<IconBtn key="remove" icon="mdi:close" />]}
/>
))}

Loading…
Cancel
Save