mirror of https://github.com/msgbyte/tailchat
refactor: 好友请求处理列表与界面
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]
|
||||||
|
}
|
||||||
@ -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';
|
||||||
Loading…
Reference in New Issue