From 2a12916428f3406e3382665ca52801a31225056b Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Wed, 14 Jul 2021 17:11:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=E7=BA=BF=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9B=91=E6=B5=8B=E4=B8=8E=E5=A4=84=E7=90=86=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/cache/useCache.ts | 45 ++++++++++++++++++++--------- shared/model/user.ts | 18 ++++++++++++ web/src/components/Avatar.tsx | 28 ++++++++++++++++-- web/src/components/UserListItem.tsx | 8 +++-- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/shared/cache/useCache.ts b/shared/cache/useCache.ts index 9e75ea14..0ffcb520 100644 --- a/shared/cache/useCache.ts +++ b/shared/cache/useCache.ts @@ -1,17 +1,36 @@ import { useQuery } from 'react-query'; -import { fetchUserInfo, UserBaseInfo } from '../model/user'; +import { + fetchUserInfo, + getUserOnlineStatus, + UserBaseInfo, +} from '../model/user'; -function buildUseCacheFactory( - scope: string, - fetcher: (id: string) => Promise -) { - return (id: string): T | Record => { - const { data } = useQuery([scope, id], () => fetcher(id)); - return data ?? {}; - }; +/** + * 用户缓存 + */ +export function useCachedUserInfo( + userId: string, + refetch = false +): UserBaseInfo | Record { + const { data } = useQuery(['user', userId], () => fetchUserInfo(userId), { + staleTime: 2 * 60 * 60 * 1000, // 缓存2小时 + refetchOnMount: refetch ? 'always' : true, + }); + + return data ?? {}; } -export const useCachedUserInfo = buildUseCacheFactory( - 'user', - fetchUserInfo -); +/** + * 用户登录状态 + */ +export function useCachedOnlineStatus(ids: string[]): boolean[] { + const { data } = useQuery( + ['onlineStatus', ids.join(',')], + () => getUserOnlineStatus(ids), + { + staleTime: 10 * 1000, // 缓存10s + } + ); + + return data ?? ids.map(() => false); +} diff --git a/shared/model/user.ts b/shared/model/user.ts index 7edaa86c..cacd3ce5 100644 --- a/shared/model/user.ts +++ b/shared/model/user.ts @@ -86,3 +86,21 @@ export async function fetchUserInfo(userId: string): Promise { return data; } + +/** + * 获取用户在线状态 + */ +export async function getUserOnlineStatus( + userIds: string[] +): Promise { + const { data } = await request.get( + '/api/gateway/checkUserOnline', + { + params: { + userIds, + }, + } + ); + + return data; +} diff --git a/web/src/components/Avatar.tsx b/web/src/components/Avatar.tsx index db1f737e..e39c8725 100644 --- a/web/src/components/Avatar.tsx +++ b/web/src/components/Avatar.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { Avatar as AntdAvatar } from 'antd'; +import { Avatar as AntdAvatar, Badge } from 'antd'; import _head from 'lodash/head'; import _upperCase from 'lodash/upperCase'; import _isNil from 'lodash/isNil'; @@ -10,6 +10,7 @@ import { getTextColorHex } from 'pawchat-shared'; interface AvatarProps extends AntdAvatarProps { name?: string; + isOnline?: boolean; } export const Avatar: React.FC = React.memo((props) => { const src = typeof props.src !== 'string' ? props.src : undefined; @@ -41,10 +42,33 @@ export const Avatar: React.FC = React.memo((props) => { style.fontSize = props.size * 0.4; } - return ( + const inner = ( {name} ); + + if (typeof props.isOnline === 'boolean') { + const style = { + bottom: 0, + top: 'auto', + }; + + if (props.isOnline === true) { + return ( + + {inner} + + ); + } else { + return ( + + {inner} + + ); + } + } + + return inner; }); Avatar.displayName = 'Avatar'; diff --git a/web/src/components/UserListItem.tsx b/web/src/components/UserListItem.tsx index 678a9802..25a014a8 100644 --- a/web/src/components/UserListItem.tsx +++ b/web/src/components/UserListItem.tsx @@ -1,9 +1,10 @@ import React, { useCallback } from 'react'; import { Avatar } from './Avatar'; -import _isNil from 'lodash/isNil'; +import _isEmpty from 'lodash/isEmpty'; import { Skeleton, Space } from 'antd'; // import { openUserProfile } from './modals/UserProfile'; import { useCachedUserInfo } from 'pawchat-shared'; +import { useCachedOnlineStatus } from '../../../shared/cache/useCache'; // const UserAvatar = styled(Avatar)` // cursor: pointer !important; @@ -22,6 +23,7 @@ interface UserListItemProps { export const UserListItem: React.FC = React.memo((props) => { const { actions = [] } = props; const userInfo = useCachedUserInfo(props.userId); + const [isOnline] = useCachedOnlineStatus([props.userId]); const userName = userInfo.nickname; const handleClick = useCallback(() => { @@ -31,13 +33,13 @@ export const UserListItem: React.FC = React.memo((props) => { return (
- +
{userName}