Files
spotizerr-dev/spotizerr-ui/src/routes/playlist.tsx
2025-06-11 10:41:32 +02:00

107 lines
3.0 KiB
TypeScript

import { Link, useParams } from "@tanstack/react-router";
import { useEffect, useState, useContext } from "react";
import apiClient from "../lib/api-client";
import { useSettings } from "../contexts/settings-context";
import { toast } from "sonner";
import type { ImageType, TrackType } from "../types/spotify";
import { QueueContext } from "../contexts/queue-context";
interface PlaylistItemType {
track: TrackType | null;
}
interface PlaylistType {
id: string;
name: string;
description: string | null;
images: ImageType[];
tracks: {
items: PlaylistItemType[];
};
}
export const Playlist = () => {
const { playlistId } = useParams({ from: "/playlist/$playlistId" });
const [playlist, setPlaylist] = useState<PlaylistType | null>(null);
const [error, setError] = useState<string | null>(null);
const context = useContext(QueueContext);
const { settings } = useSettings();
if (!context) {
throw new Error("useQueue must be used within a QueueProvider");
}
const { addItem } = context;
useEffect(() => {
const fetchPlaylist = async () => {
if (!playlistId) return;
try {
const response = await apiClient.get<PlaylistType>(`/playlist/info?id=${playlistId}`);
setPlaylist(response.data);
} catch (err) {
setError("Failed to load playlist");
console.error(err);
}
};
fetchPlaylist();
}, [playlistId]);
const handleDownloadTrack = (track: TrackType) => {
if (!track?.id) return;
addItem({ spotifyId: track.id, type: "track", name: track.name });
toast.info(`Adding ${track.name} to queue...`);
};
const handleDownloadPlaylist = () => {
if (!playlist) return;
addItem({
spotifyId: playlist.id,
type: "playlist",
name: playlist.name,
});
toast.info(`Adding ${playlist.name} to queue...`);
};
if (error) {
return <div className="text-red-500">{error}</div>;
}
if (!playlist) {
return <div>Loading...</div>;
}
const filteredTracks = playlist.tracks.items.filter(({ track }) => {
if (!track) return false;
if (settings?.explicitFilter && track.explicit) return false;
return true;
});
return (
<div className="playlist-page">
<div className="playlist-header">
<img src={playlist.images[0]?.url} alt={playlist.name} className="playlist-image" />
<div>
<h1>{playlist.name}</h1>
<p>{playlist.description}</p>
<button onClick={handleDownloadPlaylist} className="download-playlist-btn">
Download All
</button>
</div>
</div>
<div className="track-list">
{filteredTracks.map(({ track }) => {
if (!track) return null;
return (
<div key={track.id} className="track-item">
<Link to="/track/$trackId" params={{ trackId: track.id }}>
{track.name}
</Link>
<button onClick={() => handleDownloadTrack(track)}>Download</button>
</div>
);
})}
</div>
</div>
);
};