"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { Calendar, Crown, MessageSquare, VolumeX } from "lucide-react"; import Modal from "@/components/Modal"; import Avatar from "@/components/Avatar"; import RoleBadge from "@/components/RoleBadge"; import LevelBadge from "@/components/LevelBadge"; import BadgeChip from "@/components/BadgeChip"; import ImageLightbox from "@/components/markdown/ImageLightbox"; import { useAllowMessages } from "@/components/MessagesPolicyProvider"; import { apiGetUserProfile, apiOpenDirectChat, type UserProfile, type User } from "@/lib/api"; import { toast } from "@/lib/toast"; export type ChatUserCardTarget = { user: User; /** 群内身份(可从成员表查到时携带) */ roomRole?: "owner" | "admin" | "member"; muted?: boolean; }; /** * 聊天用户资料卡:头像(可进图箱)+ 基本信息 + 主页/私聊入口。 * 公开资料拉取失败时降级为消息内已有的基础字段,不阻塞弹层。 */ export default function ChatUserCard({ target, meId, onClose, }: { target: ChatUserCardTarget; meId: number; onClose: () => void; }) { const router = useRouter(); const allowMessages = useAllowMessages(); const [profile, setProfile] = useState(null); const [dmBusy, setDmBusy] = useState(false); const [avatarOpen, setAvatarOpen] = useState(false); const { user } = target; const isSelf = user.id === meId; const name = user.nickname || user.username; useEffect(() => { let alive = true; setProfile(null); apiGetUserProfile(user.id) .then((p) => { if (alive) setProfile(p); }) .catch(() => { // 静默:资料卡仍展示消息携带的基础字段 }); return () => { alive = false; }; }, [user.id]); const startDM = async () => { setDmBusy(true); try { const { room } = await apiOpenDirectChat(user.id); onClose(); router.push(`/chat/${room.id}`); } catch (e) { toast(e instanceof Error ? e.message : "无法发起私聊"); } finally { setDmBusy(false); } }; const sig = (profile?.user.signature || "").trim(); const canPreviewAvatar = !!user.avatar; const stats = profile?.stats; return ( <> {/* 头部:头像 + 昵称/角色 */}
{canPreviewAvatar ? ( ) : ( )}

{name}

{(user.badges ?? []).slice(0, 2).map((ub) => ( ))} {user.role && user.role !== "user" && ( )} {profile?.user.banned === true && ( 已封禁 )}

@{user.username}

{target.roomRole === "owner" && (

群主

)} {target.roomRole === "admin" && (

群管理员

)} {target.muted && (

禁言中

)}
{/* 签名 */} {sig && (

{sig}

)} {/* 基本信息行 */}
{stats ? stats.post_count : "–"} {" "} 帖子 {stats ? stats.comment_count : "–"} {" "} 评论 {stats ? stats.points : "–"} {" "} 积分 {profile?.user.created_at && ( {new Date(profile.user.created_at).toLocaleDateString("zh-CN")} 加入 )}
{/* 操作区 */}
用户主页 {allowMessages && !isSelf && ( )}
{avatarOpen && canPreviewAvatar && ( setAvatarOpen(false)} onIndexChange={() => {}} /> )} ); }