mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
184 lines
5.4 KiB
TypeScript
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'
|
|
)
|
|
}
|