fix(frontend): extend add-to-playlist context menu to local Musify tracks

Right-clicking a local track in the Musify section now opens the same nested add-to-playlist menu as YouTube results. Also switched the playlist thumbnail corners to rounded-lg.
This commit is contained in:
2026-07-16 17:36:36 +02:00
parent 4ba20d741e
commit 90be0c6538
+29 -12
View File
@@ -20,6 +20,15 @@
isExplicit: boolean; isExplicit: boolean;
} }
interface LocalTrackTarget {
id: string;
title: string;
}
type ContextMenuTarget =
| { kind: 'youtube'; song: YouTubeSong }
| { kind: 'local'; track: LocalTrackTarget };
let { data, form } = $props(); let { data, form } = $props();
let searchValue = $state(data.query); let searchValue = $state(data.query);
@@ -28,7 +37,7 @@
let ytItems = $state<YouTubeSong[]>([]); let ytItems = $state<YouTubeSong[]>([]);
let ytContinuation = $state(''); let ytContinuation = $state('');
let ytLoadingMore = $state(false); let ytLoadingMore = $state(false);
let contextMenu = $state<{ song: YouTubeSong; x: number; y: number; openLeft: boolean } | null>( let contextMenu = $state<(ContextMenuTarget & { x: number; y: number; openLeft: boolean }) | null>(
null null
); );
@@ -102,13 +111,13 @@
); );
} }
function openContextMenu(event: MouseEvent, song: YouTubeSong) { function openContextMenu(event: MouseEvent, target: ContextMenuTarget) {
if (data.playlists.length === 0) return; if (data.playlists.length === 0) return;
event.preventDefault(); event.preventDefault();
const menuWidth = 208; const menuWidth = 208;
const submenuWidth = 224; const submenuWidth = 224;
contextMenu = { contextMenu = {
song, ...target,
x: Math.min(event.clientX, window.innerWidth - menuWidth - 8), x: Math.min(event.clientX, window.innerWidth - menuWidth - 8),
y: Math.min(event.clientY, window.innerHeight - 60), y: Math.min(event.clientY, window.innerHeight - 60),
openLeft: event.clientX + menuWidth + submenuWidth + 16 > window.innerWidth openLeft: event.clientX + menuWidth + submenuWidth + 16 > window.innerWidth
@@ -196,7 +205,11 @@
{:else} {:else}
<ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4"> <ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4">
{#each tracks.items as track, i (track.id)} {#each tracks.items as track, i (track.id)}
<li class="group animate-enter" style="animation-delay:{i * 40}ms"> <li
class="group animate-enter relative"
style="animation-delay:{i * 40}ms"
oncontextmenu={(e) => openContextMenu(e, { kind: 'local', track })}
>
<button type="button" onclick={() => togglePlayLocal(i)} class="block w-full text-left"> <button type="button" onclick={() => togglePlayLocal(i)} class="block w-full text-left">
<Cover <Cover
trackId={track.id} trackId={track.id}
@@ -277,7 +290,7 @@
<li <li
class="group animate-enter relative" class="group animate-enter relative"
style="animation-delay:{i * 40}ms" style="animation-delay:{i * 40}ms"
oncontextmenu={(e) => openContextMenu(e, song)} oncontextmenu={(e) => openContextMenu(e, { kind: 'youtube', song })}
> >
<button type="button" onclick={() => togglePlayYouTube(i)} class="block w-full text-left"> <button type="button" onclick={() => togglePlayYouTube(i)} class="block w-full text-left">
<Cover <Cover
@@ -375,7 +388,7 @@
{#each data.playlists as playlist (playlist.id)} {#each data.playlists as playlist (playlist.id)}
<form <form
method="POST" method="POST"
action="?/addYouTubeToPlaylist" action={contextMenu.kind === 'youtube' ? '?/addYouTubeToPlaylist' : '?/addTrack'}
use:enhance={() => use:enhance={() =>
({ update }) => { ({ update }) => {
closeContextMenu(); closeContextMenu();
@@ -383,11 +396,15 @@
}} }}
> >
<input type="hidden" name="playlistId" value={playlist.id} /> <input type="hidden" name="playlistId" value={playlist.id} />
<input type="hidden" name="videoId" value={contextMenu.song.videoId} /> {#if contextMenu.kind === 'youtube'}
<input type="hidden" name="title" value={contextMenu.song.title} /> <input type="hidden" name="videoId" value={contextMenu.song.videoId} />
<input type="hidden" name="artist" value={contextMenu.song.artist} /> <input type="hidden" name="title" value={contextMenu.song.title} />
<input type="hidden" name="durationSeconds" value={contextMenu.song.durationSeconds} /> <input type="hidden" name="artist" value={contextMenu.song.artist} />
<input type="hidden" name="thumbnailUrl" value={contextMenu.song.thumbnailUrl} /> <input type="hidden" name="durationSeconds" value={contextMenu.song.durationSeconds} />
<input type="hidden" name="thumbnailUrl" value={contextMenu.song.thumbnailUrl} />
{:else}
<input type="hidden" name="trackId" value={contextMenu.track.id} />
{/if}
<button <button
type="submit" type="submit"
class="flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-left text-sm text-neutral-200 transition hover:bg-white/5" class="flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-left text-sm text-neutral-200 transition hover:bg-white/5"
@@ -397,7 +414,7 @@
trackIds={[]} trackIds={[]}
hue={hueFor(playlist.id)} hue={hueFor(playlist.id)}
size="small" size="small"
class="h-8 w-8 flex-shrink-0 rounded-md" class="h-8 w-8 flex-shrink-0 rounded-lg"
/> />
<span class="truncate">{playlist.name}</span> <span class="truncate">{playlist.name}</span>
</button> </button>