import React from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { ChevronRight, Play, Plus, RefreshCw, Square } from 'lucide-react'; import type { ColDef } from '../../utils/useTracklistColumns'; import type { SubsonicSong } from '../../api/subsonicTypes'; import { usePlayerStore } from '../../store/playerStore'; import { usePreviewStore } from '../../store/previewStore'; import { useThemeStore } from '../../store/themeStore'; import { songToTrack } from '../../utils/songToTrack'; import { codecLabel, formatDuration } from '../../utils/playlistDetailHelpers'; const PL_CENTERED = new Set(['favorite', 'rating', 'duration']); interface Props { songs: SubsonicSong[]; suggestions: SubsonicSong[]; existingIds: Set; loadingSuggestions: boolean; loadSuggestions: (songs: SubsonicSong[]) => void; visibleCols: ColDef[]; gridStyle: React.CSSProperties; contextMenuSongId: string | null; setContextMenuSongId: React.Dispatch>; hoveredSuggestionId: string | null; setHoveredSuggestionId: React.Dispatch>; addSong: (song: SubsonicSong) => void; startPreview: (song: SubsonicSong) => void; } export default function PlaylistSuggestions({ songs, suggestions, existingIds, loadingSuggestions, loadSuggestions, visibleCols, gridStyle, contextMenuSongId, setContextMenuSongId, hoveredSuggestionId, setHoveredSuggestionId, addSong, startPreview, }: Props) { const { t } = useTranslation(); const navigate = useNavigate(); const openContextMenu = usePlayerStore(s => s.openContextMenu); const previewingId = usePreviewStore(s => s.previewingId); const previewAudioStarted = usePreviewStore(s => s.audioStarted); const showBitrate = useThemeStore(s => s.showBitrate); const filteredSuggestions = suggestions.filter(s => !existingIds.has(s.id)); return (

{t('playlists.suggestions')}

{t('playlists.suggestionsHint')}
{!loadingSuggestions && filteredSuggestions.length === 0 && (
{t('playlists.noSuggestions')}
)} {filteredSuggestions.length > 0 && ( <>
{visibleCols.map((colDef) => { const key = colDef.key; const isCentered = PL_CENTERED.has(key); const label = colDef.i18nKey ? t(`albumDetail.${colDef.i18nKey}`) : ''; if (key === 'num') return
#
; if (key === 'title') return
{label}
; if (key === 'delete') return
; if (key === 'favorite' || key === 'rating') return
; return
{label}
; })}
{filteredSuggestions.map((song, idx) => (
setHoveredSuggestionId(song.id)} onMouseLeave={() => setHoveredSuggestionId(null)} onDoubleClick={e => { if ((e.target as HTMLElement).closest('button, a, input')) return; addSong(song); }} onContextMenu={e => { e.preventDefault(); setContextMenuSongId(song.id); openContextMenu(e.clientX, e.clientY, songToTrack(song), 'album-song'); }} > {visibleCols.map(colDef => { switch (colDef.key) { case 'num': return
{idx + 1}
; case 'title': return (
{song.title}
); case 'artist': return (
{ if (song.artistId) { e.stopPropagation(); navigate(`/artist/${song.artistId}`); } }}>{song.artist}
); case 'album': return (
{ if (song.albumId) { e.stopPropagation(); navigate(`/album/${song.albumId}`); } }}>{song.album}
); case 'favorite': return
; case 'rating': return
; case 'duration': return
{formatDuration(song.duration ?? 0)}
; case 'format': return (
{(song.suffix || (showBitrate && song.bitRate)) && {codecLabel(song, showBitrate)}}
); case 'delete': return (
); default: return null; } })}
))} )}
); }