Files
gerbeur/shared/playlist.ts
khannurien 53515ce46f v4: playlists have categories of their own — a set apart from the dumps', which admins manage on /manage under "Playlist categories", next to "Dump categories". Each category now has a kind, dump or playlist, its slug and name unique within its kind only; migration 0025 rebuilds categories with the column, every existing one becoming a dump category with its dumps, and adds playlist_categories. A playlist's owner files it under any of them in the create and edit forms, and it shows them as chips on its card and its page, each leading to /playlists/<slug>.
/playlists is the new page of every public playlist, newest first (GET /api/playlists), and /playlists/<slug> a playlist category's, with a menu of the categories, linked from the user menu as "All playlists"; `playlists` is reserved as a dump category's slug, and one that had it is renamed. A profile's Playlists tab narrows to a playlist category with the same menu as its Dumps tab (?category=), and so do the Dumps and Playlists tabs of /search, each with its own kind (category and playlistCategory for GET /api/search), another tab dropping it.

The category service takes the kind throughout, so a dump can't be filed under a playlist category nor the other way round, and categoryJoin is the one filter join the dump, follow and playlist services share. useCategories(kind) gives one kind's set from the one fetch, CategoryChips replaces the chips the dump card and page each drew, useCategoryFilter keeps a list to its category as the socket brings more, and useCategorySelection holds a form's picked categories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:53:16 +00:00

113 lines
3.3 KiB
TypeScript

/**
* Playlists are ordered collections of dumps their owners curate. This module
* types them, with the bodies that create, edit and reorder them. The client
* shows them on `/@owner/playlists/:slug`, on profiles and in search results.
*
* @module
*/
import type { Dump } from "./dump.ts";
/**
* Returned by the playlist routes, and by the search, users and dumps routes
* that list playlists. The `playlist_created` and `playlist_updated` WebSocket
* messages carry it too.
*/
export interface Playlist {
id: string;
userId: string;
title: string;
/**
* Names the playlist in its page's path, `playlistPath`
* (`shared/paths.ts`) building it with {@link ownerUsername}. Made from the
* title on creation, it changes only when edited, and is unique among its
* owner's playlists.
*/
slug: string;
/** It is written in Markdown. */
description?: string;
/**
* Only its owner sees a private playlist. Moderators don't, though they may
* still edit or delete it.
*/
isPublic: boolean;
createdAt: Date;
/**
* `playlistImageUrl` puts it on the cover's URL, so a new cover isn't hidden
* by the cached old one.
*/
updatedAt?: Date;
/** It is undefined when the playlist has no cover. */
imageMime?: string;
/**
* It counts the dumps the viewer can't see too. It is undefined when the
* query didn't count them.
*/
dumpCount?: number;
/** The owner's current name, which the playlist's path starts with. */
ownerUsername: string;
/**
* The ids of the playlist categories it is filed under. The services fill
* them in on every playlist they return.
*/
categoryIds?: string[];
}
/**
* Returned by `GET /api/users/:key/playlists/:slug` to `PlaylistDetail`, and
* by `GET /api/playlists/:playlistId`, with the dumps the caller may see, in
* the playlist's order.
*/
export interface PlaylistWithDumps extends Playlist {
dumps: Dump[];
}
/**
* Returned by `GET /api/me/playlists?contains=:dumpId`, one per playlist of
* the caller's. `PlaylistMembershipPanel` ticks the playlists
* with `hasDump`, in the add-to-playlist modal and the dump form.
*/
export interface PlaylistMembership {
playlist: Playlist;
hasDump: boolean;
}
/** Passed to `POST /api/playlists` by `PlaylistCreateForm`. */
export interface CreatePlaylistRequest {
title: string;
description?: string;
isPublic: boolean;
/** The playlist categories to file it under, none when left out. */
categoryIds?: string[];
}
/**
* Passed to `PUT /api/playlists/:playlistId` by `PlaylistDetail`. A field left
* out keeps its value.
*/
export interface UpdatePlaylistRequest {
title?: string;
/**
* A new slug, which the edit form's slug field sends. A title change never
* changes the slug by itself.
*/
slug?: string;
/** `null` removes the description. */
description?: string | null;
isPublic?: boolean;
/** Replaces its playlist categories; `[]` takes it out of every one. */
categoryIds?: string[];
}
/**
* Passed to `PUT /api/playlists/:playlistId/order` by `PlaylistDetail`, after
* a drag or to put back a dump whose removal was undone.
*/
export interface ReorderPlaylistRequest {
/**
* Every dump of the playlist the caller can see, in the new order. The dumps
* hidden from them keep their slots.
*/
dumpIds: string[];
}