This pass also tackles the task deferred earlier: removing
tracks.ts's TrackTarget wrapper and its 8 accessors
(targetId/targetTitle/targetArtist/targetOwnerUserId/targetExplicit/
targetListensCount/isTargetCurrent/queueItemForTarget/
targetForQueueItem). Explore, Home, Mix, menus.svelte.ts and mixes.ts
now use track objects directly, with
toQueueItem/trackFromQueueItem (new, in player.svelte.ts) as the only
converters. Along the way, TypeScript revealed that isExplicit
doesn't exist in the real /tracks schema: Explore's "explicit" badge
was already a no-op before this refactor (always undefined ??
false); the binding is removed instead of pretending support that
never existed.
Explore: GENRE_INFO/genreTiles are extracted to src/lib/genres.ts;
the search logic
(showGroup/capped/matchPlaylists/searchCounts/searchChips/findTopResult)
is extracted to src/lib/search.ts as pure functions parameterized by
filter/query instead of closing over component state. "Top result"
unifies the duplicated button/link with <svelte:element>. Decision
made without blocking: the 4 group blocks (Tracks/Albums/Users/
Playlists) are not collapsed into a single {#each groups} — each
group has genuinely different snippets, context menu and ListRow
props, and a generic config would have added more indirection than it
removed.
Home: uses Page instead of a hand-rolled page-x; the 3 stats become
an {#each}; the hero/spotlight backgrounds (inline style= with CSS
variables) become the hero-surface/spotlight-surface utilities, and
gap-11.5 is replaced by the --spacing-section token (2.875rem) via
gap-section.
Verified with npm run check (0 errors) and npm run build. /explore
without a session redirects to /login because +layout.server.ts
requires allowAnonymousListening from the backend (unreachable
without Docker this session) — pre-existing behavior, not a
regression. No authenticated visual QA; smoke-tested /login with no
console errors.
26 lines
639 B
TypeScript
26 lines
639 B
TypeScript
import { contextMenuPosition, type MenuPosition } from '$lib/components/ui/ContextMenu.svelte';
|
|
import { player, toQueueItem, type ApiTrackLike } from './player/player.svelte';
|
|
|
|
export type TrackMenuState = { track: ApiTrackLike } & MenuPosition;
|
|
|
|
export function createTrackMenu() {
|
|
let state = $state<TrackMenuState | null>(null);
|
|
|
|
return {
|
|
get state() {
|
|
return state;
|
|
},
|
|
open(event: MouseEvent, track: ApiTrackLike) {
|
|
state = { track, ...contextMenuPosition(event) };
|
|
},
|
|
close() {
|
|
state = null;
|
|
},
|
|
playNext() {
|
|
if (!state) return;
|
|
player.playNextItem(toQueueItem(state.track));
|
|
state = null;
|
|
}
|
|
};
|
|
}
|