import React from 'react'; import { NavLink } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { AudioLines, ChevronRight, HardDriveDownload, PlayCircle, Settings, Sparkles } from 'lucide-react'; import type { SidebarItemConfig } from '../../store/sidebarStore'; import { ALL_NAV_ITEMS } from '../../config/navItems'; import WhatsNewBanner from '../WhatsNewBanner'; import { displayPlaylistName, isSmartPlaylistName } from '../../utils/componentHelpers/sidebarHelpers'; import SidebarLibraryPicker from './SidebarLibraryPicker'; import SidebarActiveJobs from './SidebarActiveJobs'; interface NavDndState { section: 'library' | 'system'; fromIdx: number; } interface MusicFolder { id: string; name: string } interface Props { isCollapsed: boolean; showLibraryPicker: boolean; filterId: string; selectedFolderName: string | null; libraryDropdownOpen: boolean; setLibraryDropdownOpen: (open: boolean) => void; dropdownRect: { top: number; left: number; width: number }; libraryTriggerRef: React.RefObject; musicFolders: MusicFolder[]; pickLibrary: (id: 'all' | string) => void; visibleLibraryConfigs: SidebarItemConfig[]; libraryItemsForReorder: SidebarItemConfig[]; visibleSystemConfigs: SidebarItemConfig[]; systemItemsForReorder: SidebarItemConfig[]; playlistsExpanded: boolean; setPlaylistsExpanded: (v: boolean) => void; playlists: { id: string; name: string }[]; playlistsLoading: boolean; newReleasesUnreadCount: number; navDnd: NavDndState | null; navDndRowClass: (section: 'library' | 'system', sectionIdx: number) => string; handleNavRowPointerDown: (e: React.PointerEvent, section: 'library' | 'system', sectionIdx: number) => void; isPlaying: boolean; hasNowPlayingTrack: boolean; hasOfflineContent: boolean; activeJobsCount: number; cancelAllDownloads: () => void; isSyncing: boolean; syncJobDone: number; syncJobSkip: number; syncJobFail: number; syncJobTotal: number; } export default function SidebarNavBody(props: Props) { const { isCollapsed, showLibraryPicker, filterId, selectedFolderName, libraryDropdownOpen, setLibraryDropdownOpen, dropdownRect, libraryTriggerRef, musicFolders, pickLibrary, visibleLibraryConfigs, libraryItemsForReorder, visibleSystemConfigs, systemItemsForReorder, playlistsExpanded, setPlaylistsExpanded, playlists, playlistsLoading, newReleasesUnreadCount, navDnd, navDndRowClass, handleNavRowPointerDown, isPlaying, hasNowPlayingTrack, hasOfflineContent, activeJobsCount, cancelAllDownloads, isSyncing, syncJobDone, syncJobSkip, syncJobFail, syncJobTotal, } = props; const { t } = useTranslation(); return ( <> {!isCollapsed && (showLibraryPicker ? ( ) : ( {t('sidebar.library')} ))} {visibleLibraryConfigs.map(cfg => { const item = ALL_NAV_ITEMS[cfg.id]; if (!item) return null; const sectionIdx = libraryItemsForReorder.findIndex(x => x.id === cfg.id); const dndRow = !isCollapsed && sectionIdx >= 0; const rowClass = dndRow ? navDndRowClass('library', sectionIdx) : undefined; const dndProps = dndRow ? { 'data-sidebar-nav-dnd-row': '', 'data-sidebar-section': 'library' as const, 'data-sidebar-idx': String(sectionIdx), onPointerDown: (e: React.PointerEvent) => handleNavRowPointerDown(e, 'library', sectionIdx), } : {}; return item.to === '/playlists' && !isCollapsed ? (
`nav-link sidebar-playlists-main-link ${isActive ? 'active' : ''}`} > {t(item.labelKey)}
{playlistsExpanded && (
{playlistsLoading ? (
) : playlists.length === 0 ? (
{t('playlists.empty')}
) : ( playlists.map((pl: { id: string; name: string }) => ( `nav-link sidebar-playlist-item ${isActive ? 'active' : ''}`} > {isSmartPlaylistName(pl.name) ? : } {displayPlaylistName(pl.name)} )) )}
)}
) : isCollapsed ? ( `nav-link ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t(item.labelKey) : undefined} data-tooltip-pos="bottom" > {item.to === '/new-releases' && newReleasesUnreadCount > 0 && ( {newReleasesUnreadCount > 99 ? '99+' : newReleasesUnreadCount} )} {!isCollapsed && {t(item.labelKey)}} ) : (
`nav-link ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t(item.labelKey) : undefined} data-tooltip-pos="bottom" > {!isCollapsed && {t(item.labelKey)}} {item.to === '/new-releases' && newReleasesUnreadCount > 0 && ( {newReleasesUnreadCount > 99 ? '99+' : newReleasesUnreadCount} )}
); })} {/* Spacer: everything from here onward sticks to the bottom of the sidebar. */}
{/* What's New banner — only visible while the current release hasn't been seen. */} {/* Now Playing — fixed, always visible */} `nav-link nav-link-nowplaying ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t('sidebar.nowPlaying') : undefined} data-tooltip-pos="bottom" > {isPlaying && hasNowPlayingTrack && } {!isCollapsed && {t('sidebar.nowPlaying')}} {hasOfflineContent && ( `nav-link nav-link-offline ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t('sidebar.offlineLibrary') : undefined} data-tooltip-pos="bottom" > {!isCollapsed && {t('sidebar.offlineLibrary')}} )} {visibleSystemConfigs.length > 0 && !isCollapsed && {t('sidebar.system')}} {visibleSystemConfigs.map(cfg => { const item = ALL_NAV_ITEMS[cfg.id]; if (!item) return null; const sectionIdx = systemItemsForReorder.findIndex(x => x.id === cfg.id); const dndRow = !isCollapsed && sectionIdx >= 0; const rowClass = dndRow ? navDndRowClass('system', sectionIdx) : undefined; const dndProps = dndRow ? { 'data-sidebar-nav-dnd-row': '', 'data-sidebar-section': 'system' as const, 'data-sidebar-idx': String(sectionIdx), onPointerDown: (e: React.PointerEvent) => handleNavRowPointerDown(e, 'system', sectionIdx), } : {}; return isCollapsed ? ( `nav-link ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t(item.labelKey) : undefined} data-tooltip-pos="bottom" > {!isCollapsed && {t(item.labelKey)}} ) : (
`nav-link ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t(item.labelKey) : undefined} data-tooltip-pos="bottom" > {!isCollapsed && {t(item.labelKey)}}
); })} `nav-link ${isActive ? 'active' : ''}`} data-tooltip={isCollapsed ? t('sidebar.settings') : undefined} data-tooltip-pos="bottom" > {!isCollapsed && {t('sidebar.settings')}} ); }