import React, { useCallback, useEffect, useState } from "react"; import { API_BASE, marked, formatWebsiteLabel, formatUserRegisteredAt } from "../config"; import icons from "../icons"; import { InfoRow, StatItem } from "./common"; import AvatarImg from "./AvatarImg"; export default function PublicUserPage({ account, onReady, onPreviewImage }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [profileLikeCount, setProfileLikeCount] = useState(0); const [viewerHasLikedToday, setViewerHasLikedToday] = useState(false); const [viewerLikesRemaining, setViewerLikesRemaining] = useState(5); const [profileLikeDailyMax, setProfileLikeDailyMax] = useState(5); const [viewerIsOwner, setViewerIsOwner] = useState(false); const [likeLoading, setLikeLoading] = useState(false); const [likeError, setLikeError] = useState(""); const authHeaders = () => { try { const token = (localStorage.getItem("sproutgate_token") || "").trim(); return token ? { Authorization: `Bearer ${token}` } : {}; } catch { return {}; } }; const loadPublicUser = useCallback(async () => { if (!account) { setError("缺少账户名"); setLoading(false); onReady?.(); return; } setLoading(true); setError(""); setUser(null); setLikeError(""); try { const res = await fetch(`${API_BASE}/api/public/users/${encodeURIComponent(account)}`, { headers: { ...authHeaders() } }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "加载公开主页失败"); setUser(data.user || null); setProfileLikeCount(Number(data.profileLikeCount) || 0); setViewerHasLikedToday(Boolean(data.viewerHasLikedToday)); const rem = Number(data.viewerLikesRemainingToday); if (Number.isFinite(rem) && rem >= 0) setViewerLikesRemaining(rem); const mx = Number(data.profileLikeDailyMax); if (Number.isFinite(mx) && mx > 0) setProfileLikeDailyMax(mx); setViewerIsOwner(Boolean(data.viewerIsOwner)); } catch (err) { setError(err.message); } finally { setLoading(false); onReady?.(); } }, [account, onReady]); useEffect(() => { loadPublicUser(); }, [loadPublicUser]); const handleProfileLike = async () => { if (!account || likeLoading || viewerIsOwner || viewerHasLikedToday || viewerLikesRemaining <= 0) return; const h = authHeaders(); if (!h.Authorization) { setLikeError("请先登录用户中心后再点赞"); return; } setLikeLoading(true); setLikeError(""); try { const res = await fetch(`${API_BASE}/api/public/users/${encodeURIComponent(account)}/like`, { method: "POST", headers: { "Content-Type": "application/json", ...h } }); const data = await res.json().catch(() => ({})); if (!res.ok) { const rem = Number(data.viewerLikesRemainingToday); if (Number.isFinite(rem) && rem >= 0) setViewerLikesRemaining(rem); throw new Error(data.error || "点赞失败"); } setProfileLikeCount(Number(data.profileLikeCount) || 0); setViewerHasLikedToday(true); const remOk = Number(data.viewerLikesRemainingToday); if (Number.isFinite(remOk) && remOk >= 0) setViewerLikesRemaining(remOk); const mxOk = Number(data.profileLikeDailyMax); if (Number.isFinite(mxOk) && mxOk > 0) setProfileLikeDailyMax(mxOk); } catch (err) { setLikeError(err.message); } finally { setLikeLoading(false); } }; const displayName = user?.username || user?.account || "未命名用户"; return (
{loading &&
加载公开主页中…
} {!loading && error && (

未找到用户

{error}
返回首页
)} {!loading && user && (

{displayName}

基本信息
{user.websiteUrl ? ( {formatWebsiteLabel(user.websiteUrl)} ) : null}
统计信息
{!viewerIsOwner && (
{authHeaders().Authorization ? ( 今日还可点赞 {viewerLikesRemaining} / {profileLikeDailyMax} 人(每自然日刷新) ) : null} {viewerHasLikedToday ? ( 今日已为 Ta 点赞 ) : viewerLikesRemaining <= 0 ? ( 今日点赞名额已用完,明天可再给最多 {profileLikeDailyMax} 人点赞 ) : ( )} {likeError ? {likeError} : null} {!authHeaders().Authorization && !viewerHasLikedToday ? ( 登录同一浏览器的用户中心后即可点赞(每人每天最多 {profileLikeDailyMax} 人) ) : null}
)}
活动记录
最后签到 {user.lastCheckInAt || user.lastCheckInDate || "未签到"} 最后访问 {user.lastVisitAt || user.lastVisitDate || "未访问"}
最后访问 IP{" "} {user.lastVisitIp || "暂无"} 最后位置 {user.lastVisitDisplayLocation || "暂无"}
个人简介
)}
); }