v4: a playlist's editor has a URL, as a dump's has — it opened over the playlist's header as a state of the page, which a reload or a shared link lost, while a dump's editor is /@name/dumps/<slug>/edit. It is now /@name/playlists/<slug>/edit: Edit goes there, and Cancel or Save come back to the page in its place. Both paths render PlaylistDetail at the same place, so the playlist stays loaded, and a new slug keeps the view. Anyone who may not edit it gets the page, a guest the sign-in first. playlistPath takes the view, and parsePagePath reads it, so its link has no preview, as a dump editor's has none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-09 22:22:24 +00:00
parent 21f4db268e
commit 9a215b0cc5
10 changed files with 130 additions and 46 deletions

View File

@@ -100,12 +100,14 @@ function withDetails(
* see in it. The socket's events keep its details and dumps live: a dump
* another client removes fades out, one it adds is fetched, a new order is
* applied, one that goes private leaves and comes back at its place, and a
* deleted playlist sends the page home. A new slug replaces the URL, and a
* path that names the playlist on screen, as that one does, fetches nothing.
* deleted playlist sends the page home. A new slug replaces the URL, keeping
* the page's `view`, and a path that names the playlist on screen, as that
* one does, fetches nothing.
*/
export function usePlaylist(
username: string | undefined,
slug: string | undefined,
view?: "edit",
): UsePlaylistResult {
const navigate = useNavigate();
const { user, authFetch } = useAuth();
@@ -142,7 +144,7 @@ export function usePlaylist(
) {
setTarget({ username, slug });
}
useCanonicalPath(playlist && playlistUrl(playlist));
useCanonicalPath(playlist && playlistUrl(playlist, view));
// The socket and the API's writes name playlists by id.
const id = playlist?.id;
const url = `${API_URL}/api/playlists/${id}`;