feat(web): drop "Remove from library" from the track kebab
test-web / test (push) Successful in 32s

No single-click destructive action belongs in the kebab. Removing the item
orphaned its whole path (RemoveTrackPopover was its only caller, and the
admin/tracks API client was the popover's only caller), so per the repo's
no-dead-code convention the chain is fully removed: the menu item + its
admin/isAdmin plumbing in TrackMenu, RemoveTrackPopover(.svelte/.test),
src/lib/api/admin/tracks(.ts/.test), and the now-needless transitive mocks
in the CompactTrackCard / PlaylistTrackRow / playlist specs.

The kebab is now an 8-item, admin-agnostic menu. The DELETE /api/admin/tracks
server endpoint is untouched — a future safer admin surface can rebind it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 22:17:35 -04:00
parent 9a166f9032
commit f7278f2417
9 changed files with 6 additions and 357 deletions
+4 -29
View File
@@ -10,17 +10,14 @@
Disc3,
User,
EyeOff,
Eye,
Trash2
Eye
} from 'lucide-svelte';
import { goto } from '$app/navigation';
import { useQueryClient } from '@tanstack/svelte-query';
import FlagPopover from './FlagPopover.svelte';
import RemoveTrackPopover from './RemoveTrackPopover.svelte';
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
import TrackMenuItem from './TrackMenuItem.svelte';
import TrackMenuDivider from './TrackMenuDivider.svelte';
import { user } from '$lib/auth/store.svelte';
import { createLikedIdsQuery, likeEntity, unlikeEntity } from '$lib/api/likes';
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
import { qk } from '$lib/api/queries';
@@ -44,7 +41,6 @@
let menuOpen = $state(false);
let flagOpen = $state(false);
let removeOpen = $state(false);
let addOpen = $state(false);
const queryClient = useQueryClient();
@@ -63,8 +59,6 @@
return rows.some((r) => r.track_id === track.id);
});
const isAdmin = $derived(user.value?.is_admin === true);
function toggleMenu(e: MouseEvent) {
e.stopPropagation();
menuOpen = !menuOpen;
@@ -73,7 +67,6 @@
function closeAll() {
menuOpen = false;
flagOpen = false;
removeOpen = false;
addOpen = false;
}
@@ -124,13 +117,8 @@
goto(`/artists/${track.artist_id}`);
}
function onRemoveOpen() {
menuOpen = false;
removeOpen = true;
}
function onKeydown(e: KeyboardEvent) {
if (!menuOpen && !flagOpen && !removeOpen && !addOpen) return;
if (!menuOpen && !flagOpen && !addOpen) return;
if (e.key === 'Escape') {
e.preventDefault();
closeAll();
@@ -166,8 +154,8 @@
add-to-playlist / start-radio group → navigation group → hide.
"Start radio" is shown even under hideQueueActions (reseeding a
station from the current track is meaningful where play-next /
add-to-queue are not). The admin-only "Remove from library" is a
web superset Android has no surface for — kept past the divider. -->
add-to-queue are not). No single-click destructive action lives
here — track removal is intentionally kept out of the kebab. -->
{#if !hideQueueActions}
<TrackMenuItem icon={ListVideo} label="Play next" onclick={onPlayNext} />
<TrackMenuItem icon={ListMusic} label="Add to queue" onclick={onAddToQueue} />
@@ -199,15 +187,6 @@
label={hidden ? 'Unhide' : 'Hide'}
onclick={onToggleHide}
/>
{#if isAdmin}
<TrackMenuDivider />
<TrackMenuItem
icon={Trash2}
label="Remove from library"
onclick={onRemoveOpen}
danger
/>
{/if}
</div>
{/if}
@@ -215,10 +194,6 @@
<FlagPopover {track} onClose={closeAll} />
{/if}
{#if removeOpen}
<RemoveTrackPopover {track} onClose={closeAll} />
{/if}
{#if addOpen}
<AddToPlaylistMenu tracks={[track]} onClose={() => (addOpen = false)} />
{/if}