feat: 在线状态监测与处理用户信息缓存

pull/13/head
moonrailgun 4 years ago
parent ce149bdaa8
commit 2a12916428

@ -1,17 +1,36 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { fetchUserInfo, UserBaseInfo } from '../model/user'; import {
fetchUserInfo,
getUserOnlineStatus,
UserBaseInfo,
} from '../model/user';
/**
*
*/
export function useCachedUserInfo(
userId: string,
refetch = false
): UserBaseInfo | Record<string, never> {
const { data } = useQuery(['user', userId], () => fetchUserInfo(userId), {
staleTime: 2 * 60 * 60 * 1000, // 缓存2小时
refetchOnMount: refetch ? 'always' : true,
});
function buildUseCacheFactory<T>(
scope: string,
fetcher: (id: string) => Promise<T>
) {
return (id: string): T | Record<string, never> => {
const { data } = useQuery([scope, id], () => fetcher(id));
return data ?? {}; return data ?? {};
};
} }
export const useCachedUserInfo = buildUseCacheFactory<UserBaseInfo>( /**
'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);
}

@ -86,3 +86,21 @@ export async function fetchUserInfo(userId: string): Promise<UserBaseInfo> {
return data; return data;
} }
/**
* 线
*/
export async function getUserOnlineStatus(
userIds: string[]
): Promise<boolean[]> {
const { data } = await request.get<boolean[]>(
'/api/gateway/checkUserOnline',
{
params: {
userIds,
},
}
);
return data;
}

@ -1,5 +1,5 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Avatar as AntdAvatar } from 'antd'; import { Avatar as AntdAvatar, Badge } from 'antd';
import _head from 'lodash/head'; import _head from 'lodash/head';
import _upperCase from 'lodash/upperCase'; import _upperCase from 'lodash/upperCase';
import _isNil from 'lodash/isNil'; import _isNil from 'lodash/isNil';
@ -10,6 +10,7 @@ import { getTextColorHex } from 'pawchat-shared';
interface AvatarProps extends AntdAvatarProps { interface AvatarProps extends AntdAvatarProps {
name?: string; name?: string;
isOnline?: boolean;
} }
export const Avatar: React.FC<AvatarProps> = React.memo((props) => { export const Avatar: React.FC<AvatarProps> = React.memo((props) => {
const src = typeof props.src !== 'string' ? props.src : undefined; const src = typeof props.src !== 'string' ? props.src : undefined;
@ -41,10 +42,33 @@ export const Avatar: React.FC<AvatarProps> = React.memo((props) => {
style.fontSize = props.size * 0.4; style.fontSize = props.size * 0.4;
} }
return ( const inner = (
<AntdAvatar {...props} src={src} style={style}> <AntdAvatar {...props} src={src} style={style}>
{name} {name}
</AntdAvatar> </AntdAvatar>
); );
if (typeof props.isOnline === 'boolean') {
const style = {
bottom: 0,
top: 'auto',
};
if (props.isOnline === true) {
return (
<Badge dot={true} color="green" style={style}>
{inner}
</Badge>
);
} else {
return (
<Badge dot={true} color="#999" style={style}>
{inner}
</Badge>
);
}
}
return inner;
}); });
Avatar.displayName = 'Avatar'; Avatar.displayName = 'Avatar';

@ -1,9 +1,10 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Avatar } from './Avatar'; import { Avatar } from './Avatar';
import _isNil from 'lodash/isNil'; import _isEmpty from 'lodash/isEmpty';
import { Skeleton, Space } from 'antd'; import { Skeleton, Space } from 'antd';
// import { openUserProfile } from './modals/UserProfile'; // import { openUserProfile } from './modals/UserProfile';
import { useCachedUserInfo } from 'pawchat-shared'; import { useCachedUserInfo } from 'pawchat-shared';
import { useCachedOnlineStatus } from '../../../shared/cache/useCache';
// const UserAvatar = styled(Avatar)` // const UserAvatar = styled(Avatar)`
// cursor: pointer !important; // cursor: pointer !important;
@ -22,6 +23,7 @@ interface UserListItemProps {
export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => { export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
const { actions = [] } = props; const { actions = [] } = props;
const userInfo = useCachedUserInfo(props.userId); const userInfo = useCachedUserInfo(props.userId);
const [isOnline] = useCachedOnlineStatus([props.userId]);
const userName = userInfo.nickname; const userName = userInfo.nickname;
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
@ -31,13 +33,13 @@ export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
return ( return (
<div className="flex items-center h-14 px-2.5 rounded group bg-white bg-opacity-0 hover:bg-opacity-20"> <div className="flex items-center h-14 px-2.5 rounded group bg-white bg-opacity-0 hover:bg-opacity-20">
<Skeleton <Skeleton
loading={_isNil(userInfo)} loading={_isEmpty(userInfo)}
avatar={true} avatar={true}
title={false} title={false}
active={true} active={true}
> >
<div className="mr-2" onClick={handleClick}> <div className="mr-2" onClick={handleClick}>
<Avatar src={userInfo.avatar} name={userName} /> <Avatar src={userInfo.avatar} name={userName} isOnline={isOnline} />
</div> </div>
<div className="flex-1 text-white"> <div className="flex-1 text-white">
<span>{userName}</span> <span>{userName}</span>

Loading…
Cancel
Save