refactor: 好友请求处理列表与界面

pull/13/head
moonrailgun 3 years ago
parent 0f81918954
commit cde2d81c5f

@ -0,0 +1,63 @@
// Place your Client workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
{
"React functional component": {
"scope": "typescriptreact",
"prefix": "rfc",
"body": [
"import React from 'react'",
"interface $1Props {",
" $2",
"}",
"export const $1 = (props: $1Props) => {",
" const { $3 } = props;",
"",
" return null;",
"};",
"$1.displayName = '$1';"
]
},
"React memo functional component": {
"scope": "typescriptreact",
"prefix": "rmc",
"body": [
"import React from 'react';",
"",
"interface ${1:Component}Props {",
" $2",
"}",
"export const $1: React.FC<$1Props> = React.memo((props) => {",
" const { $3 } = props;",
"",
" return null;",
"});",
"$1.displayName = '$1';"
]
},
"React memo functional component pure": {
"scope": "typescriptreact",
"prefix": "rmcp",
"body": [
"import React from 'react';",
"",
"export const ${1:Component}: React.FC = React.memo(() => {",
" return null;",
"});",
"$1.displayName = '$1';"
]
}
}

@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"editor.rulers": [80]
}

@ -38,6 +38,7 @@ export {
// model
export { addFriendRequest } from './model/friend';
export type { FriendRequest } from './model/friend';
export type { UserBaseInfo, UserLoginInfo } from './model/user';
export {
loginWithEmail,

@ -26,7 +26,10 @@ function reduxHookCacheFactory<T>(
) {
const isFetchingDataIdQueue: string[] = []; // 正在请求的id列表
return function hook(id: string, options?: CacheHookOptions): Partial<T> {
return function useReduxCache(
id: string,
options?: CacheHookOptions
): Partial<T> {
const data = useAppSelector<T>(
(state) => _get(state, ['cache', cacheScope, id]) as any
);

@ -0,0 +1,20 @@
import { Icon } from '@iconify/react';
import { Button, ButtonProps } from 'antd';
import React from 'react';
interface IconBtnProps extends ButtonProps {
icon: string;
}
export const IconBtn: React.FC<IconBtnProps> = React.memo(
({ icon, ...props }) => {
return (
<Button
className="border-0 bg-black bg-opacity-30 text-white text-opacity-80 hover:text-opacity-100 hover:bg-opacity-60"
shape="circle"
{...props}
icon={<Icon className="anticon" icon={icon} />}
/>
);
}
);
IconBtn.displayName = 'IconBtn';

@ -0,0 +1,53 @@
import React, { useCallback } from 'react';
import { Avatar } from './Avatar';
import _isNil from 'lodash/isNil';
import { Skeleton, Space } from 'antd';
// import { openUserProfile } from './modals/UserProfile';
import { useCachedUserInfo } from 'pawchat-shared';
// const UserAvatar = styled(Avatar)`
// cursor: pointer !important;
// margin-right: 10px !important;
// `;
// const UserNameText = styled(Typography)`
// flex: 1;
// color: ${(props) => props.theme.color.headerPrimary} !important;
// `;
interface UserListItemProps {
userId: string;
actions?: React.ReactElement[];
}
export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
const { actions = [] } = props;
const userInfo = useCachedUserInfo(props.userId);
const userName = userInfo.nickname;
const handleClick = useCallback(() => {
console.log('clicked avatar');
}, []);
return (
<div className="flex items-center h-14 px-2.5 rounded group bg-white bg-opacity-0 hover:bg-opacity-20">
<Skeleton
loading={_isNil(userInfo)}
avatar={true}
title={false}
active={true}
>
<div className="mr-2" onClick={handleClick}>
<Avatar src={userInfo.avatar} name={userName} />
</div>
<div className="flex-1 text-white">
<span>{userName}</span>
<span className="text-gray-300 opacity-0 group-hover:opacity-100">
#{userInfo.discriminator}
</span>
</div>
<Space>{...actions}</Space>
</Skeleton>
</div>
);
});
UserListItem.displayName = 'UserListItem';

@ -0,0 +1,36 @@
import { IconBtn } from '@/components/IconBtn';
import { UserListItem } from '@/components/UserListItem';
import { Tooltip } from 'antd';
import { FriendRequest, t } from 'pawchat-shared';
import React from 'react';
export const RequestReceived: React.FC<{
requests: FriendRequest[];
}> = React.memo((props) => {
return (
<div className="py-2.5 px-5">
<div></div>
<div>
{props.requests.map(({ from }) => (
<UserListItem
key={from}
userId={from}
actions={[
<Tooltip key="accept" title={t('接受')}>
<div>
<IconBtn icon="mdi-check" />
</div>
</Tooltip>,
<Tooltip key="deny" title={t('拒绝')}>
<div>
<IconBtn icon="mdi-close" />
</div>
</Tooltip>,
]}
/>
))}
</div>
</div>
);
});
RequestReceived.displayName = 'RequestReceived';

@ -0,0 +1,31 @@
import { IconBtn } from '@/components/IconBtn';
import { UserListItem } from '@/components/UserListItem';
import { Tooltip } from 'antd';
import { FriendRequest, t } from 'pawchat-shared';
import React from 'react';
export const RequestSend: React.FC<{
requests: FriendRequest[];
}> = React.memo((props) => {
return (
<div className="py-2.5 px-5">
<div></div>
<div>
{props.requests.map(({ to }) => (
<UserListItem
key={to}
userId={to}
actions={[
<Tooltip key="cancel" title={t('取消')}>
<div>
<IconBtn icon="mdi-close" />
</div>
</Tooltip>,
]}
/>
))}
</div>
</div>
);
});
RequestSend.displayName = 'RequestSend';

@ -1,7 +1,9 @@
import React from 'react';
import { PillTabPane, PillTabs } from '@/components/PillTabs';
import { AddFriend } from './AddFriend';
import { useAppSelector } from 'pawchat-shared';
import { t, useAppSelector } from 'pawchat-shared';
import { RequestSend } from './RequestSend';
import { RequestReceived } from './RequestReceived';
/**
*
@ -20,25 +22,15 @@ export const FriendPanel: React.FC = React.memo(() => {
<div>{JSON.stringify(friends)}</div>
</div>
</PillTabPane>
<PillTabPane tab={'已发送'} key="2">
<div className="py-2.5 px-5">
<div></div>
<div>
{JSON.stringify(
friendRequests.filter((item) => item.from === userId)
)}
</div>
</div>
<PillTabPane tab={t('已发送')} key="2">
<RequestSend
requests={friendRequests.filter((item) => item.from === userId)}
/>
</PillTabPane>
<PillTabPane tab={'待处理'} key="3">
<div className="py-2.5 px-5">
<div></div>
<div>
{JSON.stringify(
friendRequests.filter((item) => item.to === userId)
)}
</div>
</div>
<PillTabPane tab={t('待处理')} key="3">
<RequestReceived
requests={friendRequests.filter((item) => item.to === userId)}
/>
</PillTabPane>
<PillTabPane
tab={<span className="text-green-400"></span>}

Loading…
Cancel
Save