147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { Gift, Star, LogOut } from "lucide-react";
|
||
import { signOut } from "next-auth/react";
|
||
import toast from "react-hot-toast";
|
||
import UserHeader from "@/components/me/UserHeader";
|
||
import QuotaCard from "@/components/me/QuotaCard";
|
||
import StatsGrid from "@/components/me/StatsGrid";
|
||
import SignInCalendar from "@/components/me/SignInCalendar";
|
||
import MyFanSupport from "@/components/me/MyFanSupport";
|
||
import { MOCK_USER, getFanSupports, type MockUser } from "@/lib/mock-user";
|
||
import { useVoteStore } from "@/lib/store";
|
||
|
||
interface MeContentProps {
|
||
session: {
|
||
id: string;
|
||
nickname: string;
|
||
};
|
||
}
|
||
|
||
export default function MeContent({ session }: MeContentProps) {
|
||
const remaining = useVoteStore((s) => s.remainingVotes);
|
||
const used = useVoteStore((s) => s.usedVotes);
|
||
const dailyQuota = useVoteStore((s) => s.dailyQuota);
|
||
const storeArtists = useVoteStore((s) => s.artists);
|
||
|
||
// 本地签到状态(数据库就绪后由 /api/me/signin 提供)
|
||
const [signedInToday, setSignedInToday] = useState(MOCK_USER.todaySignedIn);
|
||
const [weeklySignIn, setWeeklySignIn] = useState(MOCK_USER.weeklySignIn);
|
||
|
||
const user: MockUser = {
|
||
...MOCK_USER,
|
||
id: session.id,
|
||
nickname: session.nickname,
|
||
remainingVotes: remaining,
|
||
usedVotes: used,
|
||
dailyQuota,
|
||
todaySignedIn: signedInToday,
|
||
weeklySignIn,
|
||
totalVotes: MOCK_USER.totalVotes + used,
|
||
};
|
||
|
||
// 用 store 里最新的艺人排名重算 "我的应援" 当前排名
|
||
const supports = getFanSupports().map((s) => {
|
||
const fresh = storeArtists.find((a) => a.id === s.artist.id);
|
||
return fresh ? { ...s, artist: fresh } : s;
|
||
});
|
||
|
||
const handleInvite = async () => {
|
||
const url =
|
||
typeof window !== "undefined"
|
||
? `${window.location.origin}?invite=${session.id}`
|
||
: "";
|
||
if (typeof navigator !== "undefined" && navigator.share) {
|
||
try {
|
||
await navigator.share({
|
||
title: "CYBER STAR · 一起为偶像应援",
|
||
text: "邀请你加入虚拟偶像 Top12 出道企划,双方各得 +5 票!",
|
||
url,
|
||
});
|
||
return;
|
||
} catch {
|
||
return;
|
||
}
|
||
}
|
||
try {
|
||
await navigator.clipboard.writeText(url);
|
||
toast.success("邀请链接已复制 · 朋友注册后双方各 +5 票");
|
||
} catch {
|
||
toast.error("复制失败,请手动复制地址");
|
||
}
|
||
};
|
||
|
||
const handleSignIn = () => {
|
||
if (signedInToday) {
|
||
toast("今日已签到", { icon: "✓" });
|
||
return;
|
||
}
|
||
// 在 weeklySignIn 数组里找到今天的位置(第一个 false)
|
||
const idx = weeklySignIn.findIndex((v) => !v);
|
||
if (idx === -1) {
|
||
toast("本周已全部签到");
|
||
return;
|
||
}
|
||
const next = [...weeklySignIn];
|
||
next[idx] = true;
|
||
setWeeklySignIn(next);
|
||
setSignedInToday(true);
|
||
toast.success("签到成功 · 获得 +3 票");
|
||
};
|
||
|
||
const handleLogout = () => {
|
||
toast("正在退出登录…", { icon: "👋" });
|
||
signOut({ callbackUrl: "/" });
|
||
};
|
||
|
||
return (
|
||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-10 space-y-8">
|
||
<UserHeader user={user} />
|
||
|
||
<QuotaCard
|
||
remaining={user.remainingVotes}
|
||
daily={user.dailyQuota}
|
||
onInvite={handleInvite}
|
||
/>
|
||
|
||
<StatsGrid user={user} />
|
||
|
||
<section>
|
||
<div className="flex items-center gap-2 mb-3">
|
||
<Gift size={14} className="text-purple-300" />
|
||
<h2 className="font-display text-sm tracking-[0.25em] text-white uppercase">
|
||
每日签到
|
||
</h2>
|
||
</div>
|
||
<SignInCalendar
|
||
weekly={user.weeklySignIn}
|
||
todaySigned={user.todaySignedIn}
|
||
onSignIn={handleSignIn}
|
||
/>
|
||
</section>
|
||
|
||
<section>
|
||
<div className="flex items-center gap-2 mb-3">
|
||
<Star size={14} className="text-purple-300" />
|
||
<h2 className="font-display text-sm tracking-[0.25em] text-white uppercase">
|
||
我的应援
|
||
</h2>
|
||
</div>
|
||
<MyFanSupport supports={supports} />
|
||
</section>
|
||
|
||
<div className="pt-4 border-t border-white/[0.06] flex justify-center">
|
||
<button
|
||
type="button"
|
||
onClick={handleLogout}
|
||
className="inline-flex items-center gap-2 px-4 h-10 rounded-full bg-white/[0.04] border border-white/10 text-white/55 hover:text-pink-400 hover:border-pink-500/40 transition-colors font-display text-xs tracking-widest uppercase"
|
||
>
|
||
<LogOut size={13} />
|
||
退出登录
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|