Files
MeBox/web/src/components/layoutNavigation.ts
T
2026-09-22 16:22:29 +08:00

184 lines
5.4 KiB
TypeScript

import type { LucideIcon } from 'lucide-react'
import {
Clock,
FileOutput,
FolderOpen,
Heart,
Home,
Library,
ListChecks,
ListMusic,
Settings,
Tv,
User,
Users,
} from 'lucide-react'
export type LayoutNavItem = {
to: string
label: string
icon: LucideIcon
end?: boolean
permission?: string
adminOnly?: boolean
}
export type HeaderBackTarget = {
to: string
label: string
}
export type MobileBottomNavItem = {
to: string
label: string
icon: LucideIcon
end?: boolean
match?: (pathname: string) => boolean
}
/** Desktop admin sidebar and admin mobile drawer. */
export const LAYOUT_NAV_ITEMS: LayoutNavItem[] = [
{ to: '/profile', label: '个人资料', icon: User },
{ to: '/libraries?from=admin', label: '媒体库', icon: Library },
{ to: '/queue', label: '任务队列', icon: ListChecks, adminOnly: true },
{ to: '/admin', label: '用户管理', icon: Users, adminOnly: true },
{ to: '/files', label: '文件管理', icon: FolderOpen, adminOnly: true },
{ to: '/emby-mount', label: 'Emby 挂载', icon: Tv, adminOnly: true },
{ to: '/strm', label: 'STRM 管理', icon: FileOutput, adminOnly: true },
{ to: '/settings', label: '系统设置', icon: Settings, adminOnly: true },
]
/** Media browsing links for the mobile drawer when browsing libraries. */
export const MEDIA_NAV_ITEMS: LayoutNavItem[] = [
{ to: '/', label: '首页', icon: Home, end: true },
{ to: '/libraries', label: '媒体库', icon: Library },
{ to: '/favourites', label: '我的收藏', icon: Heart },
{ to: '/playlists', label: '播放列表', icon: ListMusic },
{ to: '/history', label: '观看历史', icon: Clock },
]
export const MOBILE_BOTTOM_NAV_ITEMS: MobileBottomNavItem[] = [
{ to: '/', label: '首页', icon: Home, end: true },
{
to: '/libraries',
label: '媒体库',
icon: Library,
match: (p) => p === '/libraries' || p.startsWith('/library'),
},
{ to: '/favourites', label: '收藏', icon: Heart },
{
to: '/playlists',
label: '列表',
icon: ListMusic,
match: (p) => p === '/playlists' || p.startsWith('/playlist'),
},
]
const ROOT_MEDIA_PATHS = new Set(['/', '/libraries', '/favourites', '/playlists', '/history'])
/** True only for the fullscreen player route (`/play` or `/play/:id`), not `/playlists`. */
export function isPlayerRoute(pathname: string): boolean {
return pathname === '/play' || pathname.startsWith('/play/')
}
export function isRootMediaPath(pathname: string): boolean {
return ROOT_MEDIA_PATHS.has(pathname)
}
/** Admin-entry query used to open the management sidebar on library pages. */
export function isAdminEntrySearch(search: string): boolean {
const params = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search)
return (
params.get('from') === 'admin' ||
params.get('from') === 'settings' ||
params.get('manage') === '1'
)
}
export function isLibraryPath(pathname: string): boolean {
return pathname === '/libraries' || pathname.startsWith('/library')
}
/**
* Sidebar active state must honor query strings. NavLink only matches pathname,
* so `/libraries` and `/libraries?from=admin` would otherwise both highlight.
*/
export function isSidebarLinkActive(
to: string,
pathname: string,
search: string,
end?: boolean,
): boolean {
const [toPath, toQuery = ''] = to.split('?')
const toIsAdminLibrary = toPath === '/libraries' && isAdminEntrySearch(toQuery)
const toIsMediaLibrary = toPath === '/libraries' && !toIsAdminLibrary
if (toIsAdminLibrary) {
return isLibraryPath(pathname) && isAdminEntrySearch(search)
}
if (toIsMediaLibrary) {
return isLibraryPath(pathname) && !isAdminEntrySearch(search)
}
return end ? pathname === toPath : pathname === toPath || pathname.startsWith(`${toPath}/`)
}
export function resolveHeaderBack(pathname: string): HeaderBackTarget | null {
if (pathname.startsWith('/library/')) {
return { to: '/libraries', label: '媒体库' }
}
// 统计页从观看历史进入,返回链也回到那里,而不是一路跳回首页。
if (pathname === '/history/stats') {
return { to: '/history', label: '观看历史' }
}
if (pathname === '/playlists' || pathname === '/favourites' || pathname === '/history') {
return { to: '/', label: '首页' }
}
if (pathname.startsWith('/playlist/')) {
return { to: '/playlists', label: '播放列表' }
}
if (pathname.startsWith('/media/')) {
return null
}
if (pathname === '/scraper/queue') {
return { to: '/queue', label: '任务队列' }
}
if (pathname === '/strm/downloads' || pathname === '/strm/uploads') {
return { to: '/strm', label: 'STRM 管理' }
}
if (
pathname === '/profile' ||
pathname === '/dlna' ||
pathname === '/play-profiles' ||
pathname === '/poster-wall'
) {
return { to: '/', label: '首页' }
}
if (
pathname === '/settings' ||
pathname === '/admin' ||
pathname === '/files' ||
pathname === '/queue' ||
pathname === '/emby-mount' ||
pathname === '/strm'
) {
return { to: '/', label: '首页' }
}
return null
}
export function shouldShowMobileBottomNav(pathname: string): boolean {
if (isPlayerRoute(pathname)) return false
return (
pathname === '/' ||
pathname === '/libraries' ||
pathname.startsWith('/library') ||
pathname.startsWith('/media') ||
pathname === '/favourites' ||
pathname === '/playlists' ||
pathname.startsWith('/playlist') ||
pathname === '/history' ||
pathname === '/poster-wall'
)
}