/** * Renders a playlist's page, where its owner and moderators also edit it and * reorder its dumps. * * @module */ import { useState } from "react"; import { useNavigate, useParams } from "react-router"; import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { can } from "../utils/auth/permissions.ts"; import { useAuth } from "../hooks/auth/useAuth.ts"; import { useDocumentTitle } from "../hooks/shared/useDocumentTitle.ts"; import { useHeightTransition } from "../hooks/shared/useHeightTransition.ts"; import { usePlaylist } from "../hooks/playlist/usePlaylist.ts"; import { PageShell } from "../components/layout/PageShell.tsx"; import { PageError } from "../components/layout/PageError.tsx"; import { ErrorCard } from "../components/shared/ErrorCard.tsx"; import { ConfirmModal } from "../components/shared/ConfirmModal.tsx"; import { PlaylistHeader } from "../components/playlist/PlaylistHeader.tsx"; import { PlaylistEditForm } from "../components/playlist/PlaylistEditForm.tsx"; import { PlaylistDumpList } from "../components/playlist/PlaylistDumpList.tsx"; /** * Renders `/@:username/playlists/:slug`, for anyone who may see the playlist: * a private one only to its owner. `usePlaylist` loads it, by a former name or * slug too, and keeps it live. Its owner, or a user with `playlist:moderate`, can edit or * delete it, drag its dumps into a new order, and remove one, with a short * window to undo. */ export function PlaylistDetail(): React.JSX.Element { const { handle, slug } = useParams(); const navigate = useNavigate(); const { user } = useAuth(); const { state, fading, actionError, applyDetails, remove, undoRemove, move, saveOrder, deletePlaylist, } = usePlaylist(handle?.slice(1), slug); const [editOpen, setEditOpen] = useState(false); const headerRef = useHeightTransition(editOpen); const [confirmDelete, setConfirmDelete] = useState(false); useDocumentTitle( state.status === "loaded" ? state.data.title : state.status === "error" ? null : undefined, ); if (state.status === "loading") { return (

Loading playlist…

); } if (state.status === "error") { return ( navigate("/")} > ← Back } /> ); } const playlist = state.data; const canEdit = (!!user && user.id === playlist.userId) || can(user, "playlist:moderate"); return (
{editOpen ? ( setEditOpen(false)} onRequestDelete={() => setConfirmDelete(true)} onSaved={(updated) => { setEditOpen(false); // Shown at once rather than left to the socket's echo, // which may never come (disconnected). applyDetails(updated); }} /> ) : (
setEditOpen(true) : undefined} />
)}
{actionError && ( )} {confirmDelete && ( { setConfirmDelete(false); void deletePlaylist(); }} onCancel={() => setConfirmDelete(false)} /> )}
); }