diff --git a/client/shared/cache/useCache.ts b/client/shared/cache/useCache.ts index a80a82c1776..f75b419dd38 100644 --- a/client/shared/cache/useCache.ts +++ b/client/shared/cache/useCache.ts @@ -1,38 +1,8 @@ -import { useQueries, useQuery, useQueryClient } from '@tanstack/react-query'; -import { - fetchUserInfo, - getUserOnlineStatus, - UserBaseInfo, -} from '../model/user'; -import { isValidStr } from '../utils/string-helper'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { getUserOnlineStatus } from '../model/user'; export { useQuery, useQueryClient }; -/** - * 用户缓存 - */ -export function useCachedUserInfo( - userId: string | null, - refetch = false -): UserBaseInfo | Record { - const { data } = useQuery( - ['user', userId], - () => { - if (!isValidStr(userId)) { - return {}; - } - - return fetchUserInfo(userId); - }, - { - staleTime: 2 * 60 * 60 * 1000, // 缓存2小时 - refetchOnMount: refetch ? 'always' : true, - } - ); - - return data ?? {}; -} - /** * 用户登录状态 */ diff --git a/client/shared/hooks/model/useUserInfo.ts b/client/shared/hooks/model/useUserInfo.ts new file mode 100644 index 00000000000..aa2a4ce3c3f --- /dev/null +++ b/client/shared/hooks/model/useUserInfo.ts @@ -0,0 +1,19 @@ +import { getCachedUserInfo } from '../../cache/cache'; +import type { UserBaseInfo } from '../../model/user'; +import { useAsync } from '../useAsync'; + +/** + * 用户信息 + */ +export function useCachedUserInfo( + userId: string, + refetch = false +): UserBaseInfo | Record { + const { value: userInfo = {} } = useAsync(async () => { + const users = getCachedUserInfo(userId, refetch); + + return users; + }, [userId, refetch]); + + return userInfo; +} diff --git a/client/shared/hooks/useSearch.ts b/client/shared/hooks/useSearch.ts index e6c63e41579..080b187fd19 100644 --- a/client/shared/hooks/useSearch.ts +++ b/client/shared/hooks/useSearch.ts @@ -1,4 +1,6 @@ import { useMemo, useState } from 'react'; +import { useFriendNicknameMap } from '../redux/hooks/useFriendNickname'; +import type { UserBaseInfo } from 'tailchat-types'; export interface UseSearchOptions { dataSource: T[]; @@ -21,3 +23,34 @@ export function useSearch(options: UseSearchOptions) { searchResult, }; } + +/** + * 用于搜索用户的封装函数 + */ +export function useUserSearch(userInfos: UserBaseInfo[]) { + const friendNicknameMap = useFriendNicknameMap(); + + const { searchText, setSearchText, isSearching, searchResult } = useSearch({ + dataSource: userInfos, + filterFn: (item, searchText) => { + if (friendNicknameMap[item._id]) { + if (friendNicknameMap[item._id].includes(searchText)) { + return true; + } + } + + if (item.nickname.includes(searchText)) { + return true; + } + + return false; + }, + }); + + return { + searchText, + setSearchText, + isSearching, + searchResult, + }; +} diff --git a/client/shared/index.tsx b/client/shared/index.tsx index 05e8b269085..7b406a048e3 100644 --- a/client/shared/index.tsx +++ b/client/shared/index.tsx @@ -14,7 +14,7 @@ export { getCachedRegistryPlugins, getCachedUserSettings, } from './cache/cache'; -export { useCachedUserInfo, useCachedOnlineStatus } from './cache/useCache'; +export { useCachedOnlineStatus } from './cache/useCache'; // components export { buildPortal, DefaultEventEmitter } from './components/Portal'; @@ -61,6 +61,7 @@ export { useLanguage } from './i18n/language'; export { createUseStorageState } from './hooks/factory/createUseStorageState'; export { useAvailableServices } from './hooks/model/useAvailableServices'; export { useMessageNotifyEventFilter } from './hooks/model/useMessageNotifyEventFilter'; +export { useCachedUserInfo } from './hooks/model/useUserInfo'; export { useUserInfoList } from './hooks/model/useUserInfoList'; export { useUsernames } from './hooks/model/useUsernames'; export { @@ -82,7 +83,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 { useSearch, useUserSearch } from './hooks/useSearch'; export { useShallowObject } from './hooks/useShallowObject'; export { useUpdateRef } from './hooks/useUpdateRef'; export { useWatch } from './hooks/useWatch'; @@ -197,7 +198,10 @@ export { useDMConverseList } from './redux/hooks/useConverse'; export { useConverseAck } from './redux/hooks/useConverseAck'; export { useConverseMessage } from './redux/hooks/useConverseMessage'; export { useDMConverseName } from './redux/hooks/useDMConverseName'; -export { useFriendNickname } from './redux/hooks/useFriendNickname'; +export { + useFriendNickname, + useFriendNicknameMap, +} from './redux/hooks/useFriendNickname'; export { useGroupInfo, useGroupMemberIds, diff --git a/client/shared/model/user.ts b/client/shared/model/user.ts index 25624b547c7..7b2ef8d943d 100644 --- a/client/shared/model/user.ts +++ b/client/shared/model/user.ts @@ -11,18 +11,9 @@ import _uniq from 'lodash/uniq'; import _flatten from 'lodash/flatten'; import _zipObject from 'lodash/zipObject'; import { t } from '../i18n'; +import type { UserBaseInfo } from 'tailchat-types'; -export interface UserBaseInfo { - _id: string; - email: string; - nickname: string; - discriminator: string; - avatar: string | null; - temporary: boolean; - emailVerified: boolean; - banned: boolean; - extra?: Record; -} +export type { UserBaseInfo }; export interface UserLoginInfo extends UserBaseInfo { token: string; diff --git a/client/web/src/components/Panel/group/TextPanel.tsx b/client/web/src/components/Panel/group/TextPanel.tsx index d5cc78a61ff..33aa3c4761e 100644 --- a/client/web/src/components/Panel/group/TextPanel.tsx +++ b/client/web/src/components/Panel/group/TextPanel.tsx @@ -19,7 +19,7 @@ import { GroupPanelType, useHasGroupPanelPermission, } from 'tailchat-shared'; -import { useFriendNicknameMap } from 'tailchat-shared/redux/hooks/useFriendNickname'; +import { useFriendNicknameMap } from 'tailchat-shared'; import { MembersPanel } from './MembersPanel'; import { GroupPanelContainer } from './shared/GroupPanelContainer'; import { MessageSearchPanel } from '../common/MessageSearch'; diff --git a/client/web/src/hooks/useGroupMemberAction.ts b/client/web/src/hooks/useGroupMemberAction.ts index f92953f0ea3..22f591004bc 100644 --- a/client/web/src/hooks/useGroupMemberAction.ts +++ b/client/web/src/hooks/useGroupMemberAction.ts @@ -13,10 +13,9 @@ import { useGroupMemberInfos, useHasGroupPermission, UserBaseInfo, - useSearch, + useUserSearch, } from 'tailchat-shared'; import _compact from 'lodash/compact'; -import { useFriendNicknameMap } from 'tailchat-shared/redux/hooks/useFriendNickname'; /** * 群组成员管理相关操作 @@ -26,7 +25,6 @@ export function useGroupMemberAction(groupId: string) { 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, @@ -37,22 +35,8 @@ export function useGroupMemberAction(groupId: string) { userInfos ); - const { searchText, setSearchText, isSearching, searchResult } = useSearch({ - dataSource: userInfos, - filterFn: (item, searchText) => { - if (friendNicknameMap[item._id]) { - if (friendNicknameMap[item._id].includes(searchText)) { - return true; - } - } - - if (item.nickname.includes(searchText)) { - return true; - } - - return false; - }, - }); + const { searchText, setSearchText, isSearching, searchResult } = + useUserSearch(userInfos); /** * 移除用户 diff --git a/client/web/src/routes/Main/Content/Personal/Friends/FriendList.tsx b/client/web/src/routes/Main/Content/Personal/Friends/FriendList.tsx index 3fdffda97d8..23f31028d87 100644 --- a/client/web/src/routes/Main/Content/Personal/Friends/FriendList.tsx +++ b/client/web/src/routes/Main/Content/Personal/Friends/FriendList.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { createDMConverse, isValidStr, @@ -12,15 +12,18 @@ import { useAsyncRequest, useEvent, useGlobalConfigStore, + useUserInfoList, + useUserSearch, userActions, } from 'tailchat-shared'; import { UserListItem } from '@/components/UserListItem'; import { IconBtn } from '@/components/IconBtn'; -import { Button, Dropdown, Tooltip } from 'antd'; +import { Button, Dropdown, Input, Tooltip } from 'antd'; import { useNavigate } from 'react-router'; import { Problem } from '@/components/Problem'; import { closeModal, openModal } from '@/components/Modal'; import { SetFriendNickname } from '@/components/modals/SetFriendNickname'; +import { Icon } from 'tailchat-design'; /** * 好友列表 @@ -29,6 +32,9 @@ export const FriendList: React.FC<{ onSwitchToAddFriend: () => void; }> = React.memo((props) => { const friends = useAppSelector((state) => state.user.friends); + const friendIds = useMemo(() => friends.map((f) => f.id), [friends]); + const userInfos = useUserInfoList(friendIds); + const { searchText, setSearchText, searchResult } = useUserSearch(userInfos); const navigate = useNavigate(); const dispatch = useAppDispatch(); const disableAddFriend = useGlobalConfigStore( @@ -91,11 +97,21 @@ export const FriendList: React.FC<{ return (
{t('好友列表')}
+ + } + value={searchText} + onChange={(e) => setSearchText(e.target.value)} + /> +
- {friends.map((item) => ( + {searchResult.map((item) => (
diff --git a/packages/types/src/model/user.ts b/packages/types/src/model/user.ts index f8ab7ae6144..38cc07f8c95 100644 --- a/packages/types/src/model/user.ts +++ b/packages/types/src/model/user.ts @@ -33,6 +33,7 @@ export interface UserBaseInfo { temporary: boolean; type: UserType; emailVerified: boolean; + banned: boolean; extra?: Record; }