import { createSelector, type Accessor, type Setter } from "solid-js"; import "./Playlist.css"; import type { PlayerState, PlaylistItem } from "../types"; import { formatTime, toast } from "../utils"; import { ClearSVG, DownloadSVG, DragSVG, SaveSVG } from "./SVG"; import { savePlaylist } from "../offline"; import { DraggableList } from "./DraggableList"; interface PlaylistProps { currentDir: Accessor; playlist: Accessor; setPlaylist: Setter; playerState: Accessor; setPlayerState: Setter; playTrack: (item: PlaylistItem) => void; audioElement: HTMLAudioElement; } export default function Playlist(props: PlaylistProps) { function removeFromPlaylist(id: string) { const currentTrack = props.playerState().currentTrack; const newPlaylist = props.playlist().filter((i) => i.id !== id); if (currentTrack && currentTrack.id === id) { const idx = props.playlist().findIndex((i) => i.id === id); if (newPlaylist.length > 0) { props.playTrack(idx < newPlaylist.length ? newPlaylist[idx] : newPlaylist[0]); } } props.setPlaylist(newPlaylist); } function browserDownload(name: string, path: string) { const a = document.createElement("a"); a.href = `/download/${path}`; a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); } async function browserDownloadAll() { try { toast("Started playlist download", "info"); const playlist = props.playlist(); const filePaths = playlist.map((item) => item.path); const form = document.createElement("form"); form.method = "POST"; form.action = "/download-playlist"; const input = document.createElement("input"); input.type = "hidden"; input.name = "files"; input.value = btoa(encodeURIComponent(JSON.stringify(filePaths))); form.appendChild(input); document.body.appendChild(form); form.submit(); document.body.removeChild(form); } catch (e) { toast("Playlist items download failed", "error", e); } } let dialogElement!: HTMLDialogElement; let playlistNameInput!: HTMLInputElement; let allowOverwriteCheckbox!: HTMLInputElement; async function save() { try { if (!playlistNameInput.reportValidity()) return; await savePlaylist({ name: playlistNameInput.value, items: props.playlist() }, allowOverwriteCheckbox.checked); closeDialog(); } catch (e) { if (e instanceof Error && e.name === "ConstraintError") { toast("Playlist with this name already exists", "error"); } else { toast("Saving playlist failed", "error", e); } } } function closeDialog() { dialogElement.close(); playlistNameInput.value = ""; allowOverwriteCheckbox.checked = false; } return (
Playlist ({props.playlist().length} tracks)
items={props.playlist} setItems={props.setPlaylist}> {(item, _, { dragHandlers, isDragging, dropPosition }) => { const isActive = createSelector( () => props.playerState().currentTrack?.id, (itemId, currentId) => currentId === itemId, ); return (
props.playTrack(item)} >
{item.name}
{formatTime(item.duration)}
); }}
); }