fix: improve discover and manual scrape flows

This commit is contained in:
ShukeBta
2026-06-24 13:58:04 +08:00
parent 34396a3a8b
commit 34c9cb4244
8 changed files with 255 additions and 79 deletions
+46 -18
View File
@@ -13,40 +13,52 @@ import {
export function DiscoverPage() {
const [sections, setSections] = useState<DiscoverSection[]>([])
const [selected, setSelected] = useState<string[]>(defaultSections)
const [selected, setSelected] = useState<string[]>([])
const [rows, setRows] = useState<Record<string, DiscoverItem[]>>({})
const [rowLoading, setRowLoading] = useState<Record<string, boolean>>({})
const [rowErrors, setRowErrors] = useState<Record<string, string>>({})
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)
const [sectionsReady, setSectionsReady] = useState(false)
const [loading, setLoading] = useState(false)
const [activeItem, setActiveItem] = useState<DiscoverItem | null>(null)
useEffect(() => {
let cancelled = false
setSectionsReady(false)
discoverAPI
.sections()
.then((items) => {
if (cancelled) return
setSections(items)
const saved = readSavedSections(items)
const available = new Set(items.map((item) => item.key))
const fallback = defaultSections.filter((key) => available.has(key))
setSelected(saved.length > 0 ? saved : fallback)
setSectionsReady(true)
})
.catch(() => {
if (cancelled) return
setSections(defaultSectionDefs)
setSelected(defaultSections)
setSectionsReady(true)
})
return () => {
cancelled = true
}
}, [])
useEffect(() => {
if (!sectionsReady) return
if (selected.length === 0) {
setRows({})
setRowLoading({})
setRowErrors({})
setLoading(false)
return
}
let cancelled = false
setLoading(true)
setError('')
setRowErrors({})
setRowLoading(Object.fromEntries(selected.map((key) => [key, true])))
setRows((current) => {
const next: Record<string, DiscoverItem[]> = {}
for (const key of selected) {
@@ -74,12 +86,17 @@ export function DiscoverPage() {
setRows((current) => ({ ...current, [key]: [] }))
setRowErrors((current) => ({ ...current, [key]: message }))
})
.finally(markDone)
.finally(() => {
if (!cancelled) {
setRowLoading((current) => ({ ...current, [key]: false }))
}
markDone()
})
}
return () => {
cancelled = true
}
}, [selected])
}, [sectionsReady, selected])
const sectionMap = useMemo(
() => new Map(sections.map((section) => [section.key, section])),
@@ -136,26 +153,24 @@ export function DiscoverPage() {
</div>
</header>
{loading && !hasContent && <DiscoverSkeleton />}
{!sectionsReady && <DiscoverSkeleton />}
{!loading && error && (
<div className="flex items-center gap-3 rounded-2xl border border-red-500/20 bg-red-500/10 p-4">
<AlertTriangle className="h-5 w-5 flex-shrink-0 text-red-400" />
<p className="text-red-300">{error}</p>
</div>
)}
{!loading && selected.length === 0 && (
{sectionsReady && !loading && selected.length === 0 && (
<div className="rounded-2xl border border-gray-200 bg-white p-10 text-center text-sand-500">
至少选择一个推荐源,小宇宙才会开始转动。
</div>
)}
{!error && selected.length > 0 && (hasContent || !loading) && (
{sectionsReady && selected.length > 0 && (
<div className="space-y-10">
{selected.map((key) => {
const items = rows[key] ?? []
if (items.length === 0) return null
if (items.length === 0) {
if (rowLoading[key]) {
return <DiscoverRowSkeleton key={key} title={sectionMap.get(key)?.label ?? key} />
}
return null
}
return (
<ContentRow
key={key}
@@ -177,7 +192,7 @@ export function DiscoverPage() {
</div>
)}
{!loading && !hasContent && (
{!loading && !hasContent && !hasRowErrors && (
<div className="rounded-2xl border border-gray-200 bg-white p-10 text-center">
<p className="text-sand-500">
当前选择的推荐源暂未返回内容,可切换豆瓣 / Bangumi 或检查网络代理。
@@ -196,3 +211,16 @@ export function DiscoverPage() {
</div>
)
}
function DiscoverRowSkeleton({ title }: { title: string }) {
return (
<section className="space-y-4">
<h2 className="pl-1 font-display text-2xl font-semibold text-ink-600">{title}</h2>
<div className="grid grid-cols-3 gap-4 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-7 xl:grid-cols-8">
{[1, 2, 3, 4, 5, 6, 7, 8].map((item) => (
<div key={item} className="aspect-[2/3] animate-pulse rounded-xl bg-gray-100" />
))}
</div>
</section>
)
}
-19
View File
@@ -117,24 +117,5 @@ function SiteTypeGuide({ type }: { type: string }) {
);
}
if (type === "yemapt") {
return (
<div className="p-3 rounded-xl border border-cyan-500/30 bg-cyan-500/5">
<div className="text-sm font-medium text-cyan-400 mb-1">
YemaPT 配置指南
</div>
<div className="text-xs text-ink-50 space-y-1">
<div>
<b>认证方式:</b>使用个人详情页创建的第三方对接专用 auth
</div>
<div className="pl-3 text-sand-500">
填入下方 API Key;后端会按 Wiki 要求通过 Authorization
请求头原样发送。
</div>
</div>
</div>
);
}
return null;
}