Initial commit: FunConnect project with server, relay, client and admin panel
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
121
client/ui/src/components/FriendCard.tsx
Normal file
121
client/ui/src/components/FriendCard.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Avatar } from './Avatar';
|
||||
import { cn } from '../lib/utils';
|
||||
import { Friend, FriendRequest } from '../stores/friendStore';
|
||||
|
||||
interface FriendCardProps {
|
||||
friend: Friend;
|
||||
onRemove?: () => void;
|
||||
onInvite?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function FriendCard({ friend, onRemove, onInvite, className }: FriendCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-bg-tertiary transition-colors group',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Avatar
|
||||
seed={friend.avatar_seed}
|
||||
name={friend.username}
|
||||
size="md"
|
||||
showOnline
|
||||
isOnline={friend.is_online}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-text-primary truncate">{friend.username}</p>
|
||||
<p className="text-xs text-text-muted">{friend.is_online ? '在线' : '离线'}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
{onInvite && friend.is_online && (
|
||||
<button
|
||||
onClick={onInvite}
|
||||
className="p-1.5 rounded text-text-muted hover:text-accent-green hover:bg-accent-green/10 transition-colors"
|
||||
title="邀请加入房间"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="8.5" cy="7" r="4" />
|
||||
<line x1="20" y1="8" x2="20" y2="14" />
|
||||
<line x1="23" y1="11" x2="17" y2="11" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
{onRemove && (
|
||||
<button
|
||||
onClick={onRemove}
|
||||
className="p-1.5 rounded text-text-muted hover:text-accent-red hover:bg-accent-red/10 transition-colors"
|
||||
title="删除好友"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="3 6 5 6 21 6" />
|
||||
<path d="M19 6l-1 14H6L5 6" />
|
||||
<path d="M10 11v6M14 11v6" />
|
||||
<path d="M9 6V4h6v2" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface FriendRequestCardProps {
|
||||
request: FriendRequest;
|
||||
onAccept: () => void;
|
||||
onReject?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function FriendRequestCard({
|
||||
request,
|
||||
onAccept,
|
||||
onReject,
|
||||
className,
|
||||
}: FriendRequestCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-3 px-3 py-2.5 rounded-lg bg-bg-tertiary',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Avatar seed={request.avatar_seed} name={request.username} size="md" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-text-primary truncate">{request.username}</p>
|
||||
<p className="text-xs text-text-muted">想加你为好友</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{onReject && (
|
||||
<button
|
||||
onClick={onReject}
|
||||
className="btn-ghost py-1 px-2 text-xs"
|
||||
>
|
||||
拒绝
|
||||
</button>
|
||||
)}
|
||||
<button onClick={onAccept} className="btn-primary py-1 px-3 text-xs">
|
||||
接受
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user