import React, { useState, useCallback, useEffect, useRef } from "react"; import { treaty } from "@elysiajs/eden"; import type { App } from "@backend/src"; import { Skeleton } from "@/components/ui/skeleton"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { MilestoneVideoCard } from "./MilestoneVideoCard"; // @ts-ignore idk const app = treaty(import.meta.env.VITE_API_URL!); export type CloseMilestoneInfo = Awaited["get"]>>["data"]; type CloseMilestoneError = Awaited["get"]>>["error"]; export type MilestoneType = "dendou" | "densetsu" | "shinwa"; export const milestoneConfig: Record = { dendou: { name: "殿堂", range: [90000, 99999], target: 100000 }, densetsu: { name: "传说", range: [900000, 999999], target: 1000000 }, shinwa: { name: "神话", range: [5000000, 9999999], target: 10000000 }, }; export const MilestoneVideos: React.FC = () => { const [milestoneType, setMilestoneType] = useState("shinwa"); const [milestoneData, setMilestoneData] = useState([]); const [closeMilestoneError, setCloseMilestoneError] = useState(); const [isLoading, setIsLoading] = useState(false); const [isLoadingMore, setIsLoadingMore] = useState(false); const [offset, setOffset] = useState(0); const [hasMore, setHasMore] = useState(true); const scrollContainer = useRef(null); const fetchMilestoneData = useCallback( async (type: MilestoneType, reset: boolean = false) => { const currentOffset = reset ? 0 : offset; if (!reset) { setIsLoadingMore(true); } else { setIsLoading(true); } setCloseMilestoneError(undefined); try { const { data, error } = await app.songs["close-milestone"]({ type }).get({ query: { offset: currentOffset, limit: 20, }, }); if (error) { setCloseMilestoneError(error); } else { if (reset) { setMilestoneData(data); } else { setMilestoneData((prev) => [...prev!, ...data]); } setHasMore(data.length >= 20); } } catch (err) { console.error("Fetch error:", err); } finally { setIsLoading(false); setIsLoadingMore(false); } }, [offset], ); useEffect(() => { setOffset(0); setHasMore(true); setMilestoneData([]); fetchMilestoneData(milestoneType, true); }, [milestoneType]); useEffect(() => { if (offset > 0 && hasMore && !isLoadingMore) { fetchMilestoneData(milestoneType); } }, [offset]); const handleScroll = useCallback( (e: React.UIEvent) => { const target = e.currentTarget; const { scrollHeight, scrollTop, clientHeight } = target; if (scrollTop + clientHeight >= scrollHeight - 500 && !isLoadingMore && hasMore) { setOffset((prev) => prev + 20); } }, [hasMore, isLoadingMore], ); const renderContent = () => { if (!milestoneData) return null; if (isLoading && milestoneData.length === 0) { return (
{[1, 2, 3].map((i) => (
))}
); } if (closeMilestoneError && milestoneData.length === 0) { return (

加载失败: {closeMilestoneError.value?.message || "未知错误"}

); } if (milestoneData.length === 0) { return (

暂无接近{milestoneConfig[milestoneType].name}的视频

); } return (
{milestoneData.map((video) => ( ))} {isLoadingMore && (
)}
); }; return ( <>

成就助攻

setMilestoneType(value as MilestoneType)}> 殿堂 传说 神话
{renderContent()} ); };