Backend: - New Likes feature (toggle/get liked tracks) and endpoints - User follow/unfollow, listening stats, public user profile - Playlist visibility (public/private) support - Various auth and DTO/response adjustments to support the above Frontend (web-player): - New design system pass: theme tokens/mode (light/dark), glass menu, tabs bar, player dock/extras/queue components, mix tiles, artist link - New routes: Liked, public user profile (/u/[id]), likes API proxy - Search refactor: shared SearchBox component used by TopBar and MobileHeader (search now reachable on mobile), live results as you type instead of requiring Enter, dropped the "Buscando…" quick-result preview - Removed now-unused components (MobileNav, CoverBadge, MixBentoTile, PlayPauseOverlay, SuggestionsTable, userDirectory) - Design handoff reference docs added under web-player/design_handoff_musify_player and web-player/docs/design-system-refactor-plan.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
64 lines
1.8 KiB
Svelte
64 lines
1.8 KiB
Svelte
<script lang="ts">
|
|
import { enhance } from '$app/forms';
|
|
import X from '@lucide/svelte/icons/x';
|
|
import {
|
|
player,
|
|
queueIdForTrack,
|
|
toQueueItems,
|
|
type ApiTrackLike
|
|
} from '$lib/player/player.svelte';
|
|
import { fmtTime } from '$lib/format';
|
|
import IconButton from '$lib/components/ui/IconButton.svelte';
|
|
import TrackTable from '$lib/components/ui/TrackTable.svelte';
|
|
import TrackRow from '$lib/components/ui/TrackRow.svelte';
|
|
import TrackMeta from '$lib/components/ui/TrackMeta.svelte';
|
|
import TrackIndexCell from '$lib/components/ui/TrackIndexCell.svelte';
|
|
import TrackTitleCell from '$lib/components/ui/TrackTitleCell.svelte';
|
|
|
|
type AlbumTrack = ApiTrackLike & { duration: number | string };
|
|
|
|
interface Props {
|
|
tracks: AlbumTrack[];
|
|
isOwner: boolean;
|
|
}
|
|
|
|
let { tracks, isOwner }: Props = $props();
|
|
|
|
function playFrom(index: number) {
|
|
player.playOrToggle(toQueueItems(tracks), index);
|
|
}
|
|
</script>
|
|
|
|
<TrackTable index action={isOwner} class="mt-6 sm:mt-8">
|
|
{#each tracks as track, i (track.id)}
|
|
{@const active = player.current.id === queueIdForTrack(track)}
|
|
<TrackRow {active}>
|
|
<TrackIndexCell index={i} {active} playing={player.playing} onToggle={() => playFrom(i)} />
|
|
<TrackTitleCell
|
|
trackId={track.id}
|
|
title={track.title}
|
|
artist={track.artist}
|
|
ownerUserId={track.ownerUserId}
|
|
explicit={track.isExplicit}
|
|
{active}
|
|
onClick={() => playFrom(i)}
|
|
/>
|
|
<TrackMeta>{fmtTime(Number(track.duration))}</TrackMeta>
|
|
{#if isOwner}
|
|
<form
|
|
method="POST"
|
|
action="?/removeTrack"
|
|
use:enhance={() =>
|
|
async ({ update }) =>
|
|
update()}
|
|
>
|
|
<input type="hidden" name="trackId" value={track.id} />
|
|
<IconButton type="submit" label="Quitar del álbum" size="sm" revealOnHover>
|
|
<X class="h-3.5 w-3.5" />
|
|
</IconButton>
|
|
</form>
|
|
{/if}
|
|
</TrackRow>
|
|
{/each}
|
|
</TrackTable>
|