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:
@@ -30,9 +30,9 @@
|
||||
let audioInput = $state<HTMLInputElement>();
|
||||
|
||||
const steps = [
|
||||
{ key: 'upload', label: 'Subir', hint: 'Portada y audio al almacenamiento' },
|
||||
{ key: 'process', label: 'Procesar', hint: 'Transcodificado para streaming' },
|
||||
{ key: 'publish', label: 'Publicar', hint: 'Disponible en el catálogo' }
|
||||
{ key: 'upload', label: 'Subir', hint: 'Portada y audio.' },
|
||||
{ key: 'process', label: 'Procesar', hint: 'Transcodificado para streaming.' },
|
||||
{ key: 'publish', label: 'Publicar', hint: 'Disponible en el catálogo.' }
|
||||
];
|
||||
|
||||
const busy = $derived(status === 'uploading');
|
||||
@@ -47,14 +47,8 @@
|
||||
return ['idle', 'idle', 'idle'];
|
||||
});
|
||||
|
||||
const dropClass = $derived(
|
||||
dragging || audioName !== '' ? 'border-accent/60' : 'border-line hover:border-accent/60'
|
||||
);
|
||||
|
||||
function stepClass(s: StepState) {
|
||||
if (s === 'active') return 'border-accent/40 bg-accent/5';
|
||||
if (s === 'done') return 'border-accent/40';
|
||||
return '';
|
||||
return s === 'idle' ? '' : 'bg-accent-tint';
|
||||
}
|
||||
|
||||
function formatSize(bytes: number) {
|
||||
@@ -112,37 +106,39 @@
|
||||
subtitle="Añade una canción con su portada y título. Nosotros la procesamos para streaming."
|
||||
/>
|
||||
|
||||
<ol class="mt-8 grid gap-3 sm:mt-10 sm:grid-cols-3 sm:gap-4">
|
||||
<ol class="mt-7 grid gap-3 sm:mt-8 sm:grid-cols-3 sm:gap-4">
|
||||
{#each steps as step, i (step.key)}
|
||||
<li>
|
||||
<Surface class={stepClass(stepStates[i])}>
|
||||
<Surface padding="sm" class={stepClass(stepStates[i])}>
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="grid h-8 w-8 place-items-center rounded-full bg-accent-soft text-sm font-bold text-on-accent"
|
||||
class="grid h-7 w-7 shrink-0 place-items-center rounded-full text-xs font-bold {stepStates[
|
||||
i
|
||||
] === 'idle'
|
||||
? 'bg-white/[3.5%] text-fg-2'
|
||||
: 'bg-accent-btn text-accent-soft'}"
|
||||
>
|
||||
{#if stepStates[i] === 'done'}
|
||||
<Check class="h-4 w-4" strokeWidth={2.5} />
|
||||
<Check class="h-3.5 w-3.5" strokeWidth={2.5} />
|
||||
{:else}
|
||||
{i + 1}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="text-fg">{step.label}</span>
|
||||
<span class="text-sm font-medium text-fg">{step.label}</span>
|
||||
</div>
|
||||
<p class="mt-3 text-base text-muted">{step.hint}</p>
|
||||
<p class="mt-2.5 text-xs text-muted">{step.hint}</p>
|
||||
</Surface>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
|
||||
{#if status === 'done'}
|
||||
<Surface padding="lg" class="mt-10 border-accent/30 bg-accent/10 text-center">
|
||||
<span
|
||||
class="mx-auto grid h-16 w-16 place-items-center rounded-full bg-accent-soft text-on-accent"
|
||||
>
|
||||
<Check class="h-8 w-8" strokeWidth={2.5} />
|
||||
<Surface padding="lg" class="mt-10 bg-accent-tint text-center">
|
||||
<span class="mx-auto grid h-14 w-14 place-items-center rounded-full bg-accent-soft text-ink">
|
||||
<Check class="h-7 w-7" strokeWidth={2.5} />
|
||||
</span>
|
||||
<h2 class="mt-6 text-2xl font-semibold tracking-tight text-fg">¡Subida!</h2>
|
||||
<p class="mt-3 text-fg-2">
|
||||
<h2 class="mt-6 font-display text-2xl font-semibold tracking-[-0.02em] text-fg">¡Subida!</h2>
|
||||
<p class="mt-3 text-sm text-fg-2">
|
||||
«{publishedTitle}» se está procesando. Aparecerá lista para reproducir en tu biblioteca en
|
||||
unos momentos.
|
||||
</p>
|
||||
@@ -184,7 +180,9 @@
|
||||
}}
|
||||
ondragleave={() => (dragging = false)}
|
||||
ondrop={onDrop}
|
||||
class="relative flex min-h-56 flex-col items-center justify-center rounded-panel border border-dashed bg-surface p-6 text-center transition sm:min-h-72 sm:p-10 {dropClass}"
|
||||
class="relative flex min-h-48 flex-col items-center justify-center rounded-panel p-6 text-center transition sm:min-h-64 sm:p-10 {dragging
|
||||
? 'bg-surface-2 ring-2 ring-accent/50'
|
||||
: 'bg-surface'}"
|
||||
>
|
||||
<input
|
||||
bind:this={audioInput}
|
||||
@@ -192,33 +190,32 @@
|
||||
name="audio"
|
||||
accept="audio/*"
|
||||
onchange={onAudioInput}
|
||||
class="absolute inset-0 cursor-pointer opacity-0"
|
||||
class="absolute inset-0 cursor-pointer opacity-0 focus:outline-none"
|
||||
aria-label="Seleccionar archivo de audio"
|
||||
/>
|
||||
<span
|
||||
class="grid h-16 w-16 place-items-center rounded-full bg-accent-soft text-on-accent sm:h-20 sm:w-20"
|
||||
>
|
||||
{#if audioName === ''}
|
||||
<Headphones class="h-8 w-8 sm:h-9 sm:w-9" />
|
||||
{:else}
|
||||
<Music class="h-8 w-8 sm:h-9 sm:w-9" />
|
||||
{/if}
|
||||
</span>
|
||||
{#if audioName === ''}
|
||||
<p class="mt-5 text-fg sm:text-lg">Arrastra tu audio aquí o haz clic para elegir</p>
|
||||
<p class="mt-2 text-sm text-muted">MP3, FLAC, WAV…</p>
|
||||
<Headphones class="h-9 w-9 text-muted sm:h-10 sm:w-10" strokeWidth={1.4} />
|
||||
{:else}
|
||||
<p class="mt-5 max-w-full truncate text-fg sm:text-lg">
|
||||
<Music class="h-9 w-9 text-accent-soft sm:h-10 sm:w-10" strokeWidth={1.4} />
|
||||
{/if}
|
||||
{#if audioName === ''}
|
||||
<p class="mt-4 text-sm font-medium text-fg">
|
||||
Arrastra tu audio aquí o haz clic para elegir
|
||||
</p>
|
||||
<p class="mt-1.5 text-xs text-muted">MP3, FLAC, WAV…</p>
|
||||
{:else}
|
||||
<p class="mt-4 max-w-full truncate text-sm font-medium text-fg">
|
||||
{audioName}
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-muted">{formatSize(audioSize)} · Listo para subir</p>
|
||||
<p class="mt-1.5 text-xs text-muted">{formatSize(audioSize)} · Listo para subir</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="grid items-start gap-6 sm:grid-cols-[auto_1fr] sm:gap-8">
|
||||
<Field label="Portada">
|
||||
<label
|
||||
class="relative grid h-36 w-36 cursor-pointer place-items-center overflow-hidden rounded-control border border-line bg-surface transition hover:border-accent/50 sm:h-44 sm:w-44"
|
||||
class="relative grid h-36 w-36 cursor-pointer place-items-center overflow-hidden rounded-control transition sm:h-44 sm:w-44"
|
||||
style={coverPreview === '' ? 'background:var(--mf-cover-grad)' : undefined}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
@@ -231,7 +228,7 @@
|
||||
{#if coverPreview !== ''}
|
||||
<img src={coverPreview} alt="Portada" class="h-full w-full object-cover" />
|
||||
{:else}
|
||||
<ImageIcon class="h-12 w-12 text-muted" />
|
||||
<ImageIcon class="h-11 w-11 text-white/70" strokeWidth={1.3} />
|
||||
{/if}
|
||||
</label>
|
||||
</Field>
|
||||
@@ -263,12 +260,12 @@
|
||||
</Checkbox>
|
||||
|
||||
{#if status === 'uploading'}
|
||||
<Surface>
|
||||
<div class="flex items-center justify-between text-base">
|
||||
<Surface padding="sm">
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="font-medium text-fg">Subiendo y creando la pista…</span>
|
||||
<span class="text-muted">un momento</span>
|
||||
<span class="text-xs text-muted">un momento</span>
|
||||
</div>
|
||||
<div class="mt-4 h-2.5 overflow-hidden rounded-full bg-surface-2">
|
||||
<div class="mt-3 h-2 overflow-hidden rounded-full bg-surface-2">
|
||||
<div class="h-full w-full animate-pulse rounded-full bg-accent-soft"></div>
|
||||
</div>
|
||||
</Surface>
|
||||
|
||||
Reference in New Issue
Block a user