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

@@ -111,9 +111,15 @@ export function dumpPath(dump: OwnedItem, view?: "edit"): string {
return view ? `${path}/${view}` : path;
}
/** Returns the path of a playlist's page, `/@owner/playlists/<slug>`. */
export function playlistPath(playlist: OwnedItem): string {
return `${userPath(playlist.ownerUsername)}/playlists/${seg(playlist.slug)}`;
/**
* Returns the path of a playlist's page, `/@owner/playlists/<slug>`, or of
* the page with its editor open, one segment more.
*/
export function playlistPath(playlist: OwnedItem, view?: "edit"): string {
const path = `${userPath(playlist.ownerUsername)}/playlists/${
seg(playlist.slug)
}`;
return view ? `${path}/${view}` : path;
}
/**
@@ -171,11 +177,11 @@ export function communityPath(view: DigestCadence = "weekly"): string {
export type PagePath =
| { kind: "user"; username: string; rest: string[] }
| { kind: "dump"; username: string; slug: string; edit: boolean }
| { kind: "playlist"; username: string; slug: string };
| { kind: "playlist"; username: string; slug: string; edit: boolean };
/**
* Returns what a page path under a user names: a profile (with its tab), a
* dump (or its editor) or a playlist. It returns null for any other path, a
* dump or a playlist (or its editor). It returns null for any other path, a
* feed or an app page, and for one under a user that no page answers. The
* names are decoded but not looked up, so the user or the item may not exist.
*/
@@ -195,18 +201,11 @@ export function parsePagePath(pathname: string): PagePath | null {
const username = handle.slice(1);
if ((section === "dumps" || section === "playlists") && slug) {
if (section === "dumps" && more.length <= 1) {
if (more.length === 0) {
return { kind: "dump", username, slug, edit: false };
}
if (more[0] === "edit") {
return { kind: "dump", username, slug, edit: true };
}
if (more.length > 1 || (more.length === 1 && more[0] !== "edit")) {
return null;
}
if (section === "playlists" && more.length === 0) {
return { kind: "playlist", username, slug };
}
return null;
const kind = section === "dumps" ? "dump" : "playlist";
return { kind, username, slug, edit: more.length === 1 };
}
const rest = segments.slice(1);