import React, { useState } from 'react'; import { Play, HardDriveDownload, Trash2, ListPlus } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useOfflineStore } from '../store/offlineStore'; import { useAuthStore } from '../store/authStore'; import { usePlayerStore } from '../store/playerStore'; import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonic'; import CachedImage from '../components/CachedImage'; type FilterType = 'all' | 'album' | 'playlist' | 'artist'; export default function OfflineLibrary() { const { t } = useTranslation(); const serverId = useAuthStore(s => s.activeServerId ?? ''); const offlineAlbums = useOfflineStore(s => s.albums); const offlineTracks = useOfflineStore(s => s.tracks); const deleteAlbum = useOfflineStore(s => s.deleteAlbum); const playTrack = usePlayerStore(s => s.playTrack); const enqueue = usePlayerStore(s => s.enqueue); const [filter, setFilter] = useState('all'); const albums = Object.values(offlineAlbums).filter(a => a.serverId === serverId); const countByType = (type: FilterType) => { if (type === 'all') return albums.length; return albums.filter(a => (a.type ?? 'album') === type).length; }; const filtered = filter === 'all' ? albums : albums.filter(a => (a.type ?? 'album') === filter); const buildTracks = (albumId: string) => { const meta = offlineAlbums[`${serverId}:${albumId}`]; if (!meta) return []; return meta.trackIds.flatMap(tid => { const t = offlineTracks[`${serverId}:${tid}`]; if (!t) return []; return [{ id: t.id, title: t.title, artist: t.artist, album: t.album, albumId: t.albumId, artistId: t.artistId, duration: t.duration, coverArt: t.coverArt, track: undefined, year: t.year, bitRate: t.bitRate, suffix: t.suffix, genre: t.genre, replayGainTrackDb: t.replayGainTrackDb, replayGainAlbumDb: t.replayGainAlbumDb, replayGainPeak: t.replayGainPeak, }]; }); }; const handlePlay = (albumId: string) => { const tracks = buildTracks(albumId); if (tracks[0]) playTrack(tracks[0], tracks); }; const handleEnqueue = (albumId: string) => { enqueue(buildTracks(albumId)); }; const renderCard = (album: typeof albums[0]) => { const coverUrl = album.coverArt ? buildCoverArtUrl(album.coverArt, 300) : ''; const cacheKey = album.coverArt ? coverArtCacheKey(album.coverArt, 300) : ''; const trackCount = album.trackIds.filter(tid => !!offlineTracks[`${serverId}:${tid}`]).length; return (
{coverUrl ? ( ) : (
)}

{album.name}

{album.artist}

{album.year &&

{album.year}

}
{t('albumDetail.tracksCount', { n: trackCount })}
); }; // For artist filter: group by artist name const renderArtistGroups = () => { const groups: Record = {}; for (const album of filtered) { const key = album.artist || '—'; if (!groups[key]) groups[key] = []; groups[key].push(album); } const sortedArtists = Object.keys(groups).sort((a, b) => a.localeCompare(b)); return sortedArtists.map(artistName => (

{artistName}

{groups[artistName].map(renderCard)}
)); }; const TABS: { id: FilterType; labelKey: string }[] = [ { id: 'all', labelKey: 'connection.offlineFilterAll' }, { id: 'album', labelKey: 'connection.offlineFilterAlbums' }, { id: 'playlist', labelKey: 'connection.offlineFilterPlaylists' }, { id: 'artist', labelKey: 'connection.offlineFilterArtists' }, ]; return (

{t('connection.offlineLibraryTitle')}

{t('connection.offlineAlbumCount', { n: albums.length, count: albums.length })}

{TABS.map(tab => { const count = countByType(tab.id); if (tab.id !== 'all' && count === 0) return null; return ( ); })}
{filtered.length === 0 ? (
{t('connection.offlineLibraryEmpty')}
) : filter === 'artist' ? ( renderArtistGroups() ) : (
{filtered.map(renderCard)}
)}
); }