You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailchat/web/src/routes/Main/Content/Personal/Friends/AddFriend.tsx

140 lines
4.0 KiB
TypeScript

import { Avatar } from '@/components/Avatar';
import { Highlight } from '@/components/Highlight';
import { Button, Divider, Empty, Typography } from 'antd';
import {
addFriendRequest,
searchUserWithUniqueName,
showErrorToasts,
showToasts,
t,
Trans,
useAppSelector,
useAsyncFn,
UserBaseInfo,
} from 'tailchat-shared';
import React, { useCallback, useState } from 'react';
import _isNil from 'lodash/isNil';
const SearchFriendResult: React.FC<{
result: UserBaseInfo | undefined | null;
}> = React.memo(({ result }) => {
const [hasSentUserId, setHasSentUserId] = useState(''); // 记录已发送的
const handleAddFriend = useCallback(async (userId: string) => {
try {
await addFriendRequest(userId);
setHasSentUserId(userId);
showToasts(t('已发送申请'), 'success');
} catch (err) {
showErrorToasts(err);
}
}, []);
if (result === undefined) {
return null;
}
if (result === null) {
return <Empty />;
}
const hasSent = hasSentUserId === result._id;
return (
<div>
<Divider />
<div className="rounded-md border border-black border-opacity-30 px-4 py-3 bg-black bg-opacity-10 flex justify-between items-center mobile:flex-col">
<div className="mobile:w-full mobile:mb-1">
<Avatar
className="mb-3"
size={60}
name={result.nickname}
src={result.avatar}
/>
<div className="text-lg">
{result.nickname}
<span className="text-opacity-60 text-sm text-white">
#{result.discriminator}
</span>
</div>
</div>
<Button
type="primary"
className="bg-green-600 border-0 mobile:w-full"
disabled={hasSent}
onClick={() => handleAddFriend(result._id)}
>
{hasSent ? t('已申请') : t('申请好友')}
</Button>
</div>
</div>
);
});
SearchFriendResult.displayName = 'SearchFriendResult';
const SelfIdentify: React.FC = React.memo(() => {
const userInfo = useAppSelector((state) => state.user.info);
const uniqueName = `${userInfo?.nickname}#${userInfo?.discriminator}`;
return (
<div>
<Divider />
<div className="rounded-md border border-black border-opacity-30 px-4 py-3 bg-black bg-opacity-10 text-center">
<div>{t('您的个人唯一标识')}</div>
<Typography.Title level={4} copyable={true} className="select-text">
{uniqueName}
</Typography.Title>
</div>
</div>
);
});
SelfIdentify.displayName = 'SelfIdentify';
export const AddFriend: React.FC = React.memo(() => {
const [uniqueName, setUniqueName] = useState('');
const [{ loading, value }, searchUser] = useAsyncFn(async () => {
// 搜索用户
try {
const data = await searchUserWithUniqueName(uniqueName);
return data;
} catch (err) {
showErrorToasts(err);
}
}, [uniqueName]);
return (
<div className="px-8 py-2">
<div className="text-lg my-2">{t('添加好友')}</div>
<div className="my-1">
<Trans>
使 <Highlight>#</Highlight>
</Trans>
</div>
<div className="px-4 py-2 my-3 flex border border-black border-opacity-30 rounded items-center bg-black bg-opacity-10 mobile:flex-col">
<input
className="bg-transparent flex-1 text-base leading-9 outline-none mobile:w-full mobile:mb-1"
placeholder={t('用户昵称#0000')}
onChange={(e) => setUniqueName(e.target.value)}
/>
<Button
type="primary"
className="bg-indigo-600 disabled:opacity-80 border-none mobile:w-full"
disabled={uniqueName === ''}
loading={loading}
onClick={searchUser}
>
{t('查找好友')}
</Button>
</div>
{_isNil(value) ? <SelfIdentify /> : <SearchFriendResult result={value} />}
</div>
);
});
AddFriend.displayName = 'AddFriend';