import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonicStreamUrl'; import { getAlbum } from '../api/subsonicLibrary'; import type { SubsonicAlbum } from '../api/subsonicTypes'; import { songToTrack } from '../utils/playback/songToTrack'; import React, { memo, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { Play, ListPlus, HardDriveDownload, Check } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '../store/playerStore'; import { useOfflineStore } from '../store/offlineStore'; import { useAuthStore } from '../store/authStore'; import CachedImage from './CachedImage'; import { OpenArtistRefInline } from './OpenArtistRefInline'; import { playAlbum } from '../utils/playback/playAlbum'; import { useDragDrop } from '../contexts/DragDropContext'; import { isAlbumRecentlyAdded } from '../utils/albumRecency'; import { deriveAlbumArtistRefs } from '../utils/album/deriveAlbumHeaderArtistRefs'; interface AlbumCardProps { album: SubsonicAlbum; selected?: boolean; selectionMode?: boolean; onToggleSelect?: (id: string, opts?: { shiftKey?: boolean }) => void; showRating?: boolean; selectedAlbums?: SubsonicAlbum[]; disableArtwork?: boolean; artworkSize?: number; } function AlbumCard({ album, selected, selectionMode, onToggleSelect, showRating = false, selectedAlbums = [], disableArtwork = false, artworkSize = 300, }: AlbumCardProps) { const { t } = useTranslation(); const navigate = useNavigate(); const openContextMenu = usePlayerStore(s => s.openContextMenu); const enqueue = usePlayerStore(s => s.enqueue); const serverId = useAuthStore(s => s.activeServerId ?? ''); const isOffline = useOfflineStore(s => { const meta = s.albums[`${serverId}:${album.id}`]; if (!meta || meta.trackIds.length === 0) return false; return meta.trackIds.every(tid => !!s.tracks[`${serverId}:${tid}`]); }); // buildCoverArtUrl emits a salted URL; memoize to avoid churn on rerenders. const coverUrl = useMemo( () => (album.coverArt ? buildCoverArtUrl(album.coverArt, artworkSize) : ''), [album.coverArt, artworkSize], ); const coverCacheKey = useMemo( () => (album.coverArt ? coverArtCacheKey(album.coverArt, artworkSize) : ''), [album.coverArt, artworkSize], ); const psyDrag = useDragDrop(); const isNewAlbum = isAlbumRecentlyAdded(album.created); const artistRefs = useMemo(() => deriveAlbumArtistRefs(album), [album]); const handleClick = (opts?: { shiftKey?: boolean }) => { if (selectionMode) { onToggleSelect?.(album.id, opts); return; } navigate(`/album/${album.id}`); }; return (
{album.name}
{album.year}
} {showRating && (album.userRating ?? 0) > 0 && (