mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-02 20:26:36 +08:00
fix legacy library scan and empty pages
This commit is contained in:
+15
-7
@@ -28,13 +28,21 @@ class AppErrorBoundary extends Component<{ children: ReactNode }, { hasError: bo
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
当前页面遇到异常,刷新后会重新加载资源和登录状态。
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => window.location.reload()}
|
||||
className="mt-5 rounded-xl bg-gray-950 px-4 py-2 text-sm font-bold text-white hover:bg-gray-800"
|
||||
>
|
||||
刷新页面
|
||||
</button>
|
||||
<div className="mt-5 flex flex-wrap items-center gap-3">
|
||||
<a
|
||||
href="/"
|
||||
className="rounded-xl bg-gray-950 px-4 py-2 text-sm font-bold text-white hover:bg-gray-800"
|
||||
>
|
||||
返回首页
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => window.location.reload()}
|
||||
className="rounded-xl border border-gray-300 px-4 py-2 text-sm font-bold text-gray-700 hover:bg-gray-100"
|
||||
>
|
||||
刷新页面
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import clsx from 'clsx'
|
||||
|
||||
import { AppFooter } from './AppFooter'
|
||||
import { LayoutSidebarContent, type LayoutSidebarContentProps } from './LayoutSidebarContent'
|
||||
import { RouteErrorBoundary } from './RouteErrorBoundary'
|
||||
import type { useLayoutSidebar } from './useLayoutSidebar'
|
||||
|
||||
type LayoutSidebarState = ReturnType<typeof useLayoutSidebar>
|
||||
@@ -120,7 +121,9 @@ export function LayoutWorkspace({ routeKey }: LayoutWorkspaceProps) {
|
||||
exit={{ opacity: 0, y: -6 }}
|
||||
transition={{ duration: 0.25, ease: 'easeOut' }}
|
||||
>
|
||||
<Outlet />
|
||||
<RouteErrorBoundary>
|
||||
<Outlet />
|
||||
</RouteErrorBoundary>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Component, type ErrorInfo, type ReactNode } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
/**
|
||||
* 路由级错误边界:只包住当前路由内容(Outlet),崩溃时侧边栏 / 顶栏仍然保留,
|
||||
* 用户可以点「返回首页」或直接用侧边栏切换到别的页面(切换 pathname 会让父层
|
||||
* 的 keyed motion.div 重新挂载本边界,从而自动恢复),避免整页只剩一个刷新按钮
|
||||
* 导致「无法返回」。
|
||||
*/
|
||||
function RouteErrorFallback() {
|
||||
const navigate = useNavigate()
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-24 text-center">
|
||||
<p className="text-lg font-bold text-ink-600">页面加载失败</p>
|
||||
<p className="mt-2 max-w-md text-sm text-sand-500">
|
||||
当前页面遇到异常。你可以返回首页继续使用,或刷新后重试;其他页面不受影响。
|
||||
</p>
|
||||
<div className="mt-5 flex flex-wrap items-center justify-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/')}
|
||||
className="rounded-xl bg-gray-950 px-4 py-2 text-sm font-bold text-white hover:bg-gray-800"
|
||||
>
|
||||
返回首页
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => window.location.reload()}
|
||||
className="rounded-xl border border-gray-300 px-4 py-2 text-sm font-bold text-ink-600 hover:bg-gray-100"
|
||||
>
|
||||
刷新页面
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export class RouteErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> {
|
||||
state = { hasError: false }
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return { hasError: true }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
console.error('MediaStationGo route crashed', error, info)
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) return <RouteErrorFallback />
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
@@ -154,9 +154,11 @@ async function loadAllSeriesCards(
|
||||
let collected: SeriesCard[] = []
|
||||
for (;;) {
|
||||
const data = await libraryAPI.listSeries(libraryID, page, pageSize)
|
||||
collected = collected.concat(data.items)
|
||||
onPage({ items: collected, total: data.total, firstPage: page === 1 })
|
||||
if (collected.length >= data.total || data.items.length < pageSize) break
|
||||
// 后端对空库可能返回 items: null(Go nil slice);不兜底会 concat 出 [null] 并崩溃。
|
||||
const pageItems = data.items ?? []
|
||||
collected = collected.concat(pageItems)
|
||||
onPage({ items: collected, total: data.total ?? collected.length, firstPage: page === 1 })
|
||||
if (collected.length >= (data.total ?? 0) || pageItems.length < pageSize) break
|
||||
page += 1
|
||||
}
|
||||
return { items: collected }
|
||||
@@ -171,9 +173,11 @@ async function loadAllMedia(
|
||||
let collected: Media[] = []
|
||||
for (;;) {
|
||||
const data = await libraryAPI.listMedia(libraryID, page, pageSize)
|
||||
collected = collected.concat(data.items)
|
||||
onPage({ items: collected, total: data.total, firstPage: page === 1 })
|
||||
if (collected.length >= data.total || data.items.length < pageSize) break
|
||||
// 后端对空库可能返回 items: null(Go nil slice);不兜底会 concat 出 [null] 并崩溃。
|
||||
const pageItems = data.items ?? []
|
||||
collected = collected.concat(pageItems)
|
||||
onPage({ items: collected, total: data.total ?? collected.length, firstPage: page === 1 })
|
||||
if (collected.length >= (data.total ?? 0) || pageItems.length < pageSize) break
|
||||
page += 1
|
||||
}
|
||||
return { items: collected }
|
||||
|
||||
@@ -74,6 +74,7 @@ function compactSeriesKey(raw: string): string {
|
||||
}
|
||||
|
||||
export function isEpisodeLike(media: Media): boolean {
|
||||
if (!media) return false
|
||||
return (media.season_num ?? 0) > 0 || (media.episode_num ?? 0) > 0
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user