import { HOUR } from "@core/lib"; import { memo, useMemo, useState } from "react"; import { When } from "react-if"; import { formatDateTime } from "@/components/SearchResults"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { columns, type Snapshot } from "@/routes/song/[id]/info/columns"; import { DataTable } from "@/routes/song/[id]/info/data-table"; import { addHoursToNow, type EtaInfo, formatHours, type Snapshots, } from "@/routes/song/[id]/info/index"; import { detectMilestoneAchievements, processSnapshots } from "@/routes/song/[id]/info/lib"; import { ViewsChart } from "@/routes/song/[id]/info/views-chart"; const StatsTable = ({ snapshots }: { snapshots: Snapshots | null }) => { if (!snapshots || snapshots.length === 0) { return null; } const tableData: Snapshot[] = snapshots.map((snapshot) => ({ coins: snapshot.coins || 0, createdAt: snapshot.createdAt, danmakus: snapshot.danmakus || 0, favorites: snapshot.favorites || 0, likes: snapshot.likes || 0, shares: snapshot.shares || 0, views: snapshot.views, })); return ; }; const getMileStoneName = (views: number) => { if (views < 100000) return "殿堂"; if (views < 1000000) return "传说"; return "神话"; }; export const SnapshotsView = ({ snapshots, etaData, publishedAt, }: { snapshots: Snapshots | null; etaData: EtaInfo | null; publishedAt: string; }) => { const [timeRange, setTimeRange] = useState("7d"); const [timeOffsetHours, setTimeOffsetHours] = useState(0); // Calculate time range in hours const timeRangeHours = useMemo(() => { switch (timeRange) { case "6h": return 6; case "24h": return 24; case "7d": return 7 * 24; case "14d": return 14 * 24; case "30d": return 30 * 24; case "90d": return 90 * 24; case "365d": return 365 * 24; default: return undefined; // "all" } }, [timeRange]); const sortedSnapshots = useMemo(() => { if (!snapshots) return null; return [ { aid: 0, coins: 0, createdAt: publishedAt, danmakus: 0, favorites: 0, id: 0, likes: 0, replies: 0, shares: 0, views: 0, }, ...snapshots, ] .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()) .map((s) => ({ ...s, timestamp: new Date(s.createdAt).getTime(), })); }, [snapshots, publishedAt]); const canGoBack = useMemo(() => { if (!sortedSnapshots || !timeRangeHours || timeRangeHours <= 0) return false; const oldestTimestamp = sortedSnapshots[0].timestamp; const newestTimestamp = sortedSnapshots[sortedSnapshots.length - 1].timestamp; const timeDiff = newestTimestamp - oldestTimestamp; return timeOffsetHours * HOUR + timeRangeHours * HOUR < timeDiff; }, [timeRangeHours, timeOffsetHours, sortedSnapshots]); const canGoForward = useMemo(() => { return !( !sortedSnapshots || !timeRangeHours || timeRangeHours <= 0 || timeOffsetHours <= 0 ); }, [timeRangeHours, timeOffsetHours, sortedSnapshots]); const processedData = useMemo( () => processSnapshots(sortedSnapshots, timeRangeHours, timeOffsetHours), [timeRangeHours, timeOffsetHours, sortedSnapshots] ); if (!snapshots) { return ; } if (snapshots.length === 0) { return (

暂无数据

); } const milestoneAchievements = detectMilestoneAchievements(snapshots, publishedAt); const handleBack = () => { if (timeRangeHours && timeRangeHours > 0) { setTimeOffsetHours((prev) => prev + timeRangeHours); } }; const handleForward = () => { if (timeRangeHours && timeRangeHours > 0) { setTimeOffsetHours((prev) => Math.max(0, prev - timeRangeHours)); } }; return (

播放: {snapshots[0].views.toLocaleString()} {" "} 更新于 {formatDateTime(new Date(snapshots[0].createdAt))}

下一个成就:{getMileStoneName(etaData?.views || 0)} {" "} 预计 {formatHours(etaData?.eta || 0)} 后( {addHoursToNow(etaData?.eta || 0)} )达成

0}>

成就达成时间:

{milestoneAchievements.map((achievement) => (

{achievement.milestoneName}( {achievement.milestone.toLocaleString()}) -{" "} {formatDateTime(new Date(achievement.achievedAt), true, false)} {achievement.timeTaken && ` - 用时 ${achievement.timeTaken}`}

))}

数据

图表 表格
); }; export const MemoizedSnapshotsView = memo(SnapshotsView);