A lot of changes
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
</a>
|
||||
<form method="POST" action="/logout">
|
||||
<button
|
||||
class="rounded-full border border-white/20 px-4 py-2 text-sm font-semibold text-white backdrop-blur-md transition hover:bg-white/5"
|
||||
class="rounded-full border border-white/10 px-4 py-2 text-sm text-white/65 transition hover:bg-white/2.5"
|
||||
>
|
||||
Salir
|
||||
</button>
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
const mainLinks = [
|
||||
{ href: '/', label: 'Inicio', icon: Home },
|
||||
{ href: '/explore', label: 'Buscar', icon: Search },
|
||||
{ href: '/playlists', label: 'Mis listas', icon: ListMusic }
|
||||
{ href: '/playlists', label: 'Listas', icon: ListMusic }
|
||||
];
|
||||
|
||||
const secondaryLinks = [
|
||||
{ href: '/library', label: 'Tus canciones subidas', icon: Library },
|
||||
{ href: '/library', label: 'Canciones subidas', icon: Library },
|
||||
{ href: '/upload', label: 'Subir música', icon: Upload }
|
||||
];
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<a
|
||||
href={link.href}
|
||||
data-active={active}
|
||||
class="relative flex items-center gap-3 rounded-[10px] px-3 py-2.5 text-[14px] font-semibold transition-colors duration-100 {active
|
||||
class="relative flex items-center gap-3 rounded-[10px] px-3 py-2.5 font-semibold transition-colors duration-100 {active
|
||||
? 'text-[var(--mf-text)]'
|
||||
: 'text-[var(--mf-text-2)] hover:bg-white/5 hover:text-[var(--mf-text)]'}"
|
||||
>
|
||||
|
||||
@@ -396,17 +396,16 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => addToQueue(contextMenu)}
|
||||
class="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold text-[var(--mf-text-2)] transition hover:bg-white/5"
|
||||
class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-sm text-[var(--mf-text-2)] transition hover:bg-white/5"
|
||||
>
|
||||
<ListPlus class="h-[18px] w-[18px] shrink-0" strokeWidth={2} />
|
||||
<ListPlus class="h-6 w-6 shrink-0" strokeWidth={2} />
|
||||
<span class="text-base">Añadir a la cola</span>
|
||||
</button>
|
||||
<div class="group/addmenu relative">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-[var(--mf-text-2)] transition group-hover/addmenu:bg-white/5"
|
||||
class="flex items-center gap-3 rounded-lg px-2 py-2.5 text-sm text-[var(--mf-text-2)] transition group-hover/addmenu:bg-white/5"
|
||||
>
|
||||
<ListMusic class="h-[18px] w-[18px] shrink-0" strokeWidth={2} />
|
||||
<span class="flex-1">Añadir a una playlist</span>
|
||||
<span class="flex-1 text-base cursor-default">Añadir a una playlist</span>
|
||||
<ChevronRight class="h-4 w-4 shrink-0 text-[var(--mf-text-3)]" />
|
||||
</div>
|
||||
<div
|
||||
@@ -445,9 +444,9 @@
|
||||
trackIds={[]}
|
||||
hue={hueFor(playlist.id)}
|
||||
size="small"
|
||||
class="h-11 w-11 flex-shrink-0 rounded-lg"
|
||||
class="h-8 w-8 flex-shrink-0 rounded-lg"
|
||||
/>
|
||||
<span class="truncate">{playlist.name}</span>
|
||||
<span class="truncate text-base text-[var(--mf-text-2)]">{playlist.name}</span>
|
||||
</button>
|
||||
</form>
|
||||
{/each}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
import { error, fail } from '@sveltejs/kit';
|
||||
import { createApiClient } from '$lib/server/api';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
@@ -16,3 +16,18 @@ export const load: PageServerLoad = async ({ locals, fetch }) => {
|
||||
|
||||
return { tracks: data };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
deleteTrack: async ({ request, locals, fetch }) => {
|
||||
if (!locals.accessToken) return fail(401, { message: 'Inicia sesión.' });
|
||||
const trackId = String((await request.formData()).get('trackId') ?? '');
|
||||
if (!trackId) return fail(400, { message: 'Falta la canción.' });
|
||||
|
||||
const api = createApiClient({ fetch, accessToken: locals.accessToken });
|
||||
const { error: err } = await api.DELETE('/tracks/{trackId}', {
|
||||
params: { path: { trackId } }
|
||||
});
|
||||
if (err) return fail(502, { message: 'No se pudo borrar la canción.' });
|
||||
return { deleted: true };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import Upload from '@lucide/svelte/icons/upload';
|
||||
import Headphones from '@lucide/svelte/icons/headphones';
|
||||
import Clock from '@lucide/svelte/icons/clock';
|
||||
import { player } from '$lib/player/player.svelte';
|
||||
import Play from '@lucide/svelte/icons/play';
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import { player, queueIdForTrack, toQueueItems } from '$lib/player/player.svelte';
|
||||
import { HUES, fmtTime } from '$lib/theme/color';
|
||||
import Cover from '$lib/components/ui/Cover.svelte';
|
||||
import NowPlaying from '$lib/components/ui/NowPlaying.svelte';
|
||||
@@ -22,8 +23,8 @@
|
||||
|
||||
function togglePlay(index: number) {
|
||||
const track = items[index];
|
||||
if (player.current.id === track.id) player.toggle();
|
||||
else player.playQueue(items, index);
|
||||
if (player.current.id === queueIdForTrack(track)) player.toggle();
|
||||
else player.playQueue(toQueueItems(items), index);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -53,50 +54,66 @@
|
||||
{#if items.length > 0}
|
||||
<div class="mt-8">
|
||||
<div
|
||||
class="grid grid-cols-[1fr_120px_160px] items-center gap-6 border-b border-[var(--mf-border)] px-3 pb-3 text-xs font-semibold uppercase tracking-wide text-[var(--mf-text-3)]"
|
||||
class="grid grid-cols-[32px_1fr_100px_100px_64px_36px] items-center gap-8 border-b border-white/10 px-3 pb-2 text-xs uppercase tracking-wide text-neutral-500"
|
||||
>
|
||||
<span class="text-center">#</span>
|
||||
<span>Título</span>
|
||||
<span class="flex items-center justify-end gap-1.5"><Clock class="h-3.5 w-3.5" /> Duración</span>
|
||||
<span class="flex items-center gap-1.5"><Headphones class="h-3.5 w-3.5" /> Escuchas</span>
|
||||
<span class="text-center">Subida</span>
|
||||
<span class="text-center">Escuchas</span>
|
||||
<span class="text-center">Duración</span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-col">
|
||||
<div class="mt-1 flex flex-col">
|
||||
{#each items as track, i (track.id)}
|
||||
<div
|
||||
class="group grid animate-enter grid-cols-[1fr_120px_160px] items-center gap-6 rounded-[10px] px-3 py-4 transition duration-100 hover:bg-[var(--mf-surface-hover)]"
|
||||
style="animation-delay:{i * 35}ms"
|
||||
class="group grid grid-cols-[32px_1fr_100px_100px_64px_36px] items-center gap-8 rounded-[10px] px-3 py-[9px] transition hover:bg-neutral-900/60"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => togglePlay(i)}
|
||||
class="flex min-w-0 items-center gap-3.5 text-left active:scale-[0.99]"
|
||||
aria-label={player.current.id === track.id && player.playing ? 'Pausar' : 'Reproducir'}
|
||||
class="relative grid h-8 w-8 place-items-center overflow-hidden rounded text-sm text-neutral-500"
|
||||
>
|
||||
{#if player.current.id === track.id}
|
||||
<NowPlaying paused={!player.playing} />
|
||||
{:else}
|
||||
<span class="group-hover:hidden">{i + 1}</span>
|
||||
<Play class="hidden h-3.5 w-3.5 text-white group-hover:block" fill="currentColor" />
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => togglePlay(i)}
|
||||
class="flex min-w-0 items-center gap-3.5 text-left"
|
||||
>
|
||||
<Cover
|
||||
trackId={track.id}
|
||||
hue={hueFor(track.id)}
|
||||
size="small"
|
||||
alt={track.title}
|
||||
class="h-[46px] w-[46px] flex-shrink-0 rounded-md"
|
||||
>
|
||||
{#if player.current.id === track.id}
|
||||
<NowPlaying paused={!player.playing} />
|
||||
{/if}
|
||||
</Cover>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-[14.5px] font-semibold text-[var(--mf-text)]">
|
||||
{track.title}
|
||||
</div>
|
||||
<div class="truncate text-[12.5px] text-[var(--mf-text-3)]">
|
||||
{dateFormatter.format(new Date(track.createdAt))}
|
||||
</div>
|
||||
</div>
|
||||
class="h-[42px] w-[42px] flex-shrink-0 rounded-md"
|
||||
/>
|
||||
<div class="min-w-0 truncate text-[14.5px] font-semibold text-neutral-100">{track.title}</div>
|
||||
</button>
|
||||
<span class="text-right text-[12.5px] tabular-nums text-[var(--mf-text-3)]">
|
||||
<div class="truncate text-center text-[12.5px] text-neutral-500">
|
||||
{dateFormatter.format(new Date(track.createdAt))}
|
||||
</div>
|
||||
<span class="text-center text-[12.5px] tabular-nums text-neutral-500">
|
||||
{Number(track.listensCount)}
|
||||
</span>
|
||||
<span class="text-center text-[12.5px] tabular-nums text-neutral-500">
|
||||
{fmtTime(Number(track.duration))}
|
||||
</span>
|
||||
<div class="flex items-center gap-1.5 text-[12.5px] tabular-nums text-[var(--mf-text-3)]">
|
||||
<Headphones class="h-3.5 w-3.5" />
|
||||
{Number(track.listensCount)}
|
||||
</div>
|
||||
<form method="POST" action="?/deleteTrack" use:enhance={() => async ({ update }) => update()}>
|
||||
<input type="hidden" name="trackId" value={track.id} />
|
||||
<button
|
||||
type="submit"
|
||||
aria-label="Borrar canción"
|
||||
class="grid h-7 w-7 place-items-center rounded-lg text-neutral-500 opacity-0 transition hover:bg-white/5 hover:text-white group-hover:opacity-100"
|
||||
>
|
||||
<X class="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -130,14 +130,14 @@
|
||||
if (e.key === 'Escape') creating = false;
|
||||
}}
|
||||
>
|
||||
<div class="animate-pop w-full max-w-2xl rounded-2xl bg-[var(--mf-bg)] p-6 shadow-2xl">
|
||||
<div class="animate-pop w-full max-w-2xl rounded-lg bg-[var(--mf-bg)] p-6 shadow-2xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-2xl font-bold tracking-tight">Nueva lista</h2>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (creating = false)}
|
||||
aria-label="Cerrar"
|
||||
class="grid h-8 w-8 place-items-center rounded-full text-neutral-400 transition hover:bg-white/5 hover:text-white"
|
||||
class="grid h-8 w-8 place-items-center rounded-lg text-neutral-400 transition hover:bg-white/5 hover:text-white"
|
||||
>
|
||||
<X class="h-4 w-4" />
|
||||
</button>
|
||||
@@ -222,7 +222,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (creating = false)}
|
||||
class="rounded-lg border border-white/15 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-white/5"
|
||||
class="rounded-lg border border-white/10 px-5 py-2.5 text-sm text-white/65 transition hover:bg-white/2.5"
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
|
||||
@@ -33,7 +33,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||
params: { path: { playlistId: params.id }, query: { pageNumber: 1, pageSize: 200 } }
|
||||
}),
|
||||
api.GET('/tracks/users/{userId}', {
|
||||
params: { path: { userId: locals.user.sub }, query: { pageNumber: 1, pageSize: 200 } }
|
||||
params: { path: { userId: locals.user.sub }, query: { pageNumber: 1, pageSize: 50 } }
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={reset}
|
||||
class="rounded-lg border border-white/20 px-8 py-3.5 text-base font-semibold text-white backdrop-blur-md transition hover:bg-white/5"
|
||||
class="rounded-lg border border-white/10 px-8 py-3.5 text-base text-white/65 transition hover:bg-white/2.5"
|
||||
>
|
||||
Subir otra
|
||||
</button>
|
||||
@@ -311,7 +311,7 @@
|
||||
<div class="flex items-center justify-end gap-4">
|
||||
<a
|
||||
href="/explore"
|
||||
class="rounded-lg border border-white/20 px-8 py-3.5 text-base font-semibold text-white backdrop-blur-md transition hover:bg-white/5"
|
||||
class="rounded-lg border border-white/10 px-8 py-3.5 text-base text-white/65 transition hover:bg-white/2.5"
|
||||
>
|
||||
Cancelar
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user