playlists(#37): profile-modal "Playlists" tab — rename / delete / reorder / remove

A central management surface, a sibling tab to "My Videos" (gated by
FEATURE_PLAYLISTS — hidden when off).

- components/profile-modal.html: "Playlists" nav item + #profilePanePlaylists.
- js/ui/profileModal/ProfilePlaylistsController.js: lists the user's playlists
  (incl. empty) with a collapsible row each — Rename, Delete, and an expand to a
  video sublist with ↑/↓ reorder and Remove. Fetches the referenced creators'
  videos so the sublist shows real titles/thumbnails. Edits are optimistic and
  re-sync on failure; publishes via the facade; dispatches
  bitvid:playlists-changed so the channel section + this pane stay in sync.
- profileModalController.js: instantiate + bind nav/pane + cache DOM + register
  listeners + selectPane("playlists") refresh; hide the tab when the flag is off.
- promptDialog.js: showTextPrompt (plain text prompt) for rename.
- playlistFacade.js: deletePlaylist — NIP-09 kind-5 (a + e refs) plus an empty
  republish so kind-5-ignoring readers also drop it.
- tailwind.source.css: .playlist-row* styles (token-only).

Lint + build clean; profile-modal-controller tests 26/26.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-07-06 15:16:06 -04:00
1 parent 2a543c3ba9
commit 73a99358a2
6 files changed
+777

No files matched your search

+83
View File
@@ -211,6 +211,28 @@
</svg>
My Videos
</button>
<button
id="profileNavPlaylists"
type="button"
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
aria-controls="profilePanePlaylists"
aria-selected="false"
>
<svg
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h11M4 10h11M4 14h7m3 5l6-3.5L14 12v7z"
/>
</svg>
Playlists
</button>
<button
id="profileNavHashtags"
type="button"
@@ -1135,6 +1157,67 @@
</div>
</section>
<section
id="profilePanePlaylists"
class="profile-pane hidden space-y-4"
role="tabpanel"
aria-labelledby="profileNavPlaylists"
>
<p class="text-sm text-muted">
Manage your playlists — rename them, reorder or remove videos, or
delete a playlist. Add videos to a playlist from any video’s ⋯
menu.
</p>
<div class="flex items-center justify-between gap-3">
<h3 class="text-base font-semibold text-text">Playlists</h3>
<button
id="profilePlaylistsRefreshBtn"
type="button"
class="btn-ghost btn-icon"
data-size="sm"
aria-label="Refresh playlists"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"
></path>
<path d="M3 3v5h5"></path>
<path
d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"
></path>
<path d="M16 21h5v-5"></path>
</svg>
</button>
</div>
<p
id="profilePlaylistsLoading"
class="text-sm text-muted hidden"
role="status"
aria-live="polite"
>
Loading your playlists…
</p>
<p id="profilePlaylistsEmpty" class="text-sm text-muted hidden">
You haven’t made any playlists yet. Open any video’s ⋯ menu and
choose “Add to playlist” to start one.
</p>
<ul
id="profilePlaylistsList"
class="grid-stack"
data-orientation="vertical"
></ul>
</section>
<section
id="profilePaneHashtags"
class="profile-pane hidden space-y-6"