Add likes, follows and playlist visibility; redesign web-player UI
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
This commit is contained in:
@@ -1,43 +1,21 @@
|
||||
<script lang="ts">
|
||||
import Plus from '@lucide/svelte/icons/plus';
|
||||
import ListMusic from '@lucide/svelte/icons/list-music';
|
||||
import Page from '$lib/components/ui/Page.svelte';
|
||||
import PageHeader from '$lib/components/ui/PageHeader.svelte';
|
||||
import MediaGrid from '$lib/components/ui/MediaGrid.svelte';
|
||||
import PlaylistCard from '$lib/components/ui/PlaylistCard.svelte';
|
||||
import Modal from '$lib/components/ui/Modal.svelte';
|
||||
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
||||
import PlaylistForm from '$lib/components/ui/PlaylistForm.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import { createPlaylistModal } from '$lib/playlists.svelte';
|
||||
import { fmtDurationLong } from '$lib/format';
|
||||
|
||||
const playlistNames = [
|
||||
'Mi Playlist',
|
||||
'Tus favoritos',
|
||||
'Lo mejor de hoy',
|
||||
'Para relajarse',
|
||||
'Buena vibra',
|
||||
'Modo estudio',
|
||||
'Road Trip',
|
||||
'Domingos',
|
||||
'Late Night',
|
||||
'Descubriendo música',
|
||||
'Todo un poco',
|
||||
'En bucle',
|
||||
'Mi colección',
|
||||
'Canciones para repetir',
|
||||
'Hits del momento'
|
||||
];
|
||||
const namePlaceholder = playlistNames[Math.floor(Math.random() * playlistNames.length)];
|
||||
|
||||
let { data, form } = $props();
|
||||
let { data } = $props();
|
||||
|
||||
const items = $derived(data.playlists.items);
|
||||
|
||||
let creating = $state(false);
|
||||
|
||||
function openCreate() {
|
||||
creating = true;
|
||||
}
|
||||
const totals = $derived(data.totals);
|
||||
const summary = $derived(
|
||||
`${totals.playlistCount} ${totals.playlistCount === 1 ? 'playlist' : 'playlists'} · ${totals.trackCount} ${totals.trackCount === 1 ? 'canción' : 'canciones'} · ${fmtDurationLong(totals.durationSeconds)}`
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -46,24 +24,24 @@
|
||||
</svelte:head>
|
||||
|
||||
<Page>
|
||||
<PageHeader title="Mis listas" subtitle="Crea y organiza tus colecciones.">
|
||||
<PageHeader
|
||||
title="Mis listas"
|
||||
subtitle={items.length > 0 ? summary : 'Crea y organiza tus colecciones.'}
|
||||
>
|
||||
{#snippet actions()}
|
||||
{#if items.length > 0}
|
||||
<Button onclick={openCreate}>
|
||||
<Plus class="h-4 w-4" strokeWidth={2.5} />
|
||||
Crear lista
|
||||
</Button>
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear lista</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</PageHeader>
|
||||
|
||||
{#if items.length > 0}
|
||||
<MediaGrid class="mt-9">
|
||||
<MediaGrid min="210px" minMobile="180px" class="mt-7 sm:mt-8">
|
||||
{#each items as playlist, i (playlist.id)}
|
||||
<PlaylistCard
|
||||
id={playlist.id}
|
||||
name={playlist.name}
|
||||
description={playlist.description}
|
||||
trackCount={playlist.trackCount}
|
||||
trackIds={playlist.coverTrackIds}
|
||||
updatedAt={playlist.updatedAt}
|
||||
index={i}
|
||||
@@ -77,22 +55,8 @@
|
||||
description="Crea la primera y añádele canciones de tu biblioteca."
|
||||
>
|
||||
{#snippet actions()}
|
||||
<Button onclick={openCreate}>
|
||||
<Plus class="h-4 w-4" strokeWidth={2} />
|
||||
Crear lista
|
||||
</Button>
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear lista</Button>
|
||||
{/snippet}
|
||||
</EmptyState>
|
||||
{/if}
|
||||
</Page>
|
||||
|
||||
<Modal open={creating} onClose={() => (creating = false)} title="Nueva lista">
|
||||
<PlaylistForm
|
||||
action="?/create"
|
||||
{namePlaceholder}
|
||||
formMessage={form?.message}
|
||||
submitLabel="Crear"
|
||||
submittingLabel="Creando…"
|
||||
onCancel={() => (creating = false)}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user