web-player: fix playlist ownership, sidebar counts, home spotlight and assorted page bugs
This commit is contained in:
@@ -31,12 +31,6 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const playlistsPromise = user
|
||||
? api.GET('/playlists/users/{userId}', {
|
||||
params: { path: { userId: user.sub }, query: { pageNumber: 1, pageSize: 50 } }
|
||||
})
|
||||
: Promise.resolve(null);
|
||||
|
||||
const albumsPromise = query
|
||||
? api.GET('/albums', {
|
||||
params: { query: { title: query, pageNumber: 1, pageSize: EXPLORE_ALBUMS_PAGE_SIZE } }
|
||||
@@ -49,9 +43,8 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => {
|
||||
})
|
||||
: Promise.resolve(null);
|
||||
|
||||
const [tracksRes, playlistsRes, albumsRes, usersRes] = await Promise.all([
|
||||
const [tracksRes, albumsRes, usersRes] = await Promise.all([
|
||||
tracksPromise,
|
||||
playlistsPromise,
|
||||
albumsPromise,
|
||||
usersPromise
|
||||
]);
|
||||
@@ -64,9 +57,10 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => {
|
||||
genres,
|
||||
tracks,
|
||||
albums: (albumsRes?.data?.items ?? []).map((item) => item.album),
|
||||
albumsTotal: Number(albumsRes?.data?.totalItemCount ?? 0),
|
||||
users: usersRes?.data?.items ?? [],
|
||||
viewerId: user?.sub ?? null,
|
||||
playlists: playlistsRes?.data?.items ?? []
|
||||
usersTotal: Number(usersRes?.data?.totalItemCount ?? 0),
|
||||
viewerId: user?.sub ?? null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
const hasAlbums = $derived(albums.length > 0);
|
||||
const hasUsers = $derived(users.length > 0);
|
||||
|
||||
const playlistMatches = $derived(matchPlaylists(data.playlists, data.query));
|
||||
const playlistMatches = $derived(matchPlaylists(data.userPlaylists, data.query));
|
||||
const hasPlaylists = $derived(playlistMatches.length > 0);
|
||||
|
||||
const nothingFound = $derived(
|
||||
@@ -92,10 +92,10 @@
|
||||
|
||||
const counts = $derived(
|
||||
searchCounts({
|
||||
tracks: songRows.length,
|
||||
albums: albums.length,
|
||||
tracks: Number(data.tracks.totalItemCount),
|
||||
albums: data.albumsTotal,
|
||||
playlists: playlistMatches.length,
|
||||
users: users.length
|
||||
users: data.usersTotal
|
||||
})
|
||||
);
|
||||
const chips = $derived(searchChips(counts));
|
||||
@@ -400,7 +400,7 @@
|
||||
>
|
||||
{#snippet trailing()}
|
||||
<span class="hidden shrink-0 text-xs text-fg-2 tabular-nums sm:block">
|
||||
{Number(album.trackCount)} canciones
|
||||
{plural(Number(album.trackCount), 'canción', 'canciones')}
|
||||
</span>
|
||||
{/snippet}
|
||||
</ListRow>
|
||||
@@ -462,7 +462,7 @@
|
||||
fields: { trackId: String(trackMenu.state.track.id) },
|
||||
label: 'Añadir a una playlist'
|
||||
}}
|
||||
playlists={data.playlists}
|
||||
playlists={data.userPlaylists}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -481,6 +481,6 @@
|
||||
fields: { albumId: albumMenu.albumId },
|
||||
label: 'Añadir álbum a una playlist'
|
||||
}}
|
||||
playlists={data.playlists}
|
||||
playlists={data.userPlaylists}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import PageHeader from '$lib/components/ui/layout/PageHeader.svelte';
|
||||
import EmptyState from '$lib/components/ui/primitives/EmptyState.svelte';
|
||||
import Button from '$lib/components/ui/primitives/Button.svelte';
|
||||
import ConfirmDialog from '$lib/components/ui/overlay/ConfirmDialog.svelte';
|
||||
import TrackList from '$lib/components/ui/media/TrackList.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
@@ -16,6 +17,8 @@
|
||||
const total = $derived(Number(data.tracks.totalItemCount));
|
||||
const listTracks = $derived(items.map((track) => ({ ...track, uploadedAt: track.createdAt })));
|
||||
|
||||
let deleting = $state<(typeof items)[number] | null>(null);
|
||||
|
||||
function togglePlay(index: number) {
|
||||
player.playOrToggle(toQueueItems(items), index);
|
||||
}
|
||||
@@ -46,7 +49,11 @@
|
||||
tracks={listTracks}
|
||||
columns={['uploaded', 'plays']}
|
||||
onPlay={togglePlay}
|
||||
rowAction={{ action: '?/deleteTrack', icon: X, label: 'Borrar canción' }}
|
||||
rowAction={{
|
||||
onclick: (track) => (deleting = items.find((item) => item.id === track.id) ?? null),
|
||||
icon: X,
|
||||
label: 'Borrar canción'
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<EmptyState
|
||||
@@ -63,3 +70,13 @@
|
||||
</EmptyState>
|
||||
{/if}
|
||||
</Page>
|
||||
|
||||
<ConfirmDialog
|
||||
open={deleting !== null}
|
||||
onClose={() => (deleting = null)}
|
||||
title="¿Borrar «{deleting?.title ?? ''}»?"
|
||||
description="Esta acción no se puede deshacer, la canción se eliminará de tu biblioteca, de tus álbumes y de las playlists donde esté."
|
||||
confirmLabel="Borrar"
|
||||
action="?/deleteTrack"
|
||||
fields={{ trackId: deleting?.id ?? '' }}
|
||||
/>
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
import { createApiClient, requireUser, unwrapOrError } from '$lib/server/api';
|
||||
import { addTrackAction } from '$lib/server/playlistActions';
|
||||
import { PLAYLIST_PICKER_PAGE_SIZE } from '$lib/config';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, locals, url, fetch }) => {
|
||||
const user = requireUser(locals, url);
|
||||
requireUser(locals, url);
|
||||
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
|
||||
|
||||
const [mixRes, playlistsRes] = await Promise.all([
|
||||
api.GET('/mixes/{id}', { params: { path: { id: params.id } } }),
|
||||
api.GET('/playlists/users/{userId}', {
|
||||
params: {
|
||||
path: { userId: user.sub },
|
||||
query: { pageNumber: 1, pageSize: PLAYLIST_PICKER_PAGE_SIZE }
|
||||
}
|
||||
})
|
||||
]);
|
||||
const mixRes = await api.GET('/mixes/{id}', { params: { path: { id: params.id } } });
|
||||
|
||||
const mix = unwrapOrError(mixRes, 'Mezcla no encontrada.', 404);
|
||||
|
||||
return { mix, playlists: playlistsRes.data?.items ?? [] };
|
||||
return { mix: unwrapOrError(mixRes, 'Mezcla no encontrada.', 404) };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
|
||||
@@ -116,6 +116,6 @@
|
||||
fields: { trackId: String(trackMenu.state.track.id) },
|
||||
label: 'Añadir a una playlist'
|
||||
}}
|
||||
playlists={data.playlists}
|
||||
playlists={data.userPlaylists}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -13,7 +13,7 @@ const MAX_NAME = 100;
|
||||
|
||||
export const load: PageServerLoad = async ({ params, locals, url, fetch, parent }) => {
|
||||
const { allowAnonymousListening } = await parent();
|
||||
optionalUser(locals, url, allowAnonymousListening);
|
||||
const user = optionalUser(locals, url, allowAnonymousListening);
|
||||
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
|
||||
|
||||
const [playlistRes, tracksRes] = await Promise.all([
|
||||
@@ -26,7 +26,9 @@ export const load: PageServerLoad = async ({ params, locals, url, fetch, parent
|
||||
const playlist = unwrapOrError(playlistRes, 'Playlist no encontrada.', 404);
|
||||
const tracks = tracksRes.data?.items ?? [];
|
||||
|
||||
return { playlist, tracks };
|
||||
const isOwner = user !== null && String(playlist.ownerUserId) === user.sub;
|
||||
|
||||
return { playlist, tracks, isOwner, section: isOwner ? '/playlists' : null };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import Artwork from '$lib/components/ui/media/Artwork.svelte';
|
||||
import PlaylistForm from '$lib/components/ui/forms/PlaylistForm.svelte';
|
||||
import Button from '$lib/components/ui/primitives/Button.svelte';
|
||||
import BackLink from '$lib/components/ui/primitives/BackLink.svelte';
|
||||
import EmptyState from '$lib/components/ui/primitives/EmptyState.svelte';
|
||||
import TrackList from '$lib/components/ui/media/TrackList.svelte';
|
||||
import { playlistMeta } from '$lib/utils/format';
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
const playlist = $derived(data.playlist);
|
||||
const tracks = $derived(data.tracks);
|
||||
const isOwner = $derived(data.isOwner);
|
||||
const listTracks = $derived(tracks.map((track) => ({ ...track, addedAt: track.createdAt })));
|
||||
|
||||
let editing = $state(false);
|
||||
@@ -39,8 +41,13 @@
|
||||
</svelte:head>
|
||||
|
||||
<Page>
|
||||
<BackLink
|
||||
href={isOwner ? '/playlists' : '/'}
|
||||
label={isOwner ? 'Volver a tus playlists' : 'Volver al inicio'}
|
||||
/>
|
||||
|
||||
<PageHeader
|
||||
eyebrow="Lista"
|
||||
eyebrow="Playlist"
|
||||
title={playlist.name}
|
||||
description={playlist.description ?? undefined}
|
||||
meta="{playlistMeta(tracks.length)} · {playlist.visibility === 'Public'
|
||||
@@ -67,10 +74,12 @@
|
||||
Reproducir
|
||||
{/if}
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary" onclick={() => (editing = true)}>Editar</Button>
|
||||
<Button size="sm" variant="secondary" onclick={() => (confirmingDelete = true)}>
|
||||
Eliminar
|
||||
</Button>
|
||||
{#if isOwner}
|
||||
<Button size="sm" variant="secondary" onclick={() => (editing = true)}>Editar</Button>
|
||||
<Button size="sm" variant="secondary" onclick={() => (confirmingDelete = true)}>
|
||||
Eliminar
|
||||
</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</PageHeader>
|
||||
|
||||
@@ -79,7 +88,9 @@
|
||||
tracks={listTracks}
|
||||
columns={['added', 'plays']}
|
||||
onPlay={playFrom}
|
||||
rowAction={{ action: '?/removeTrack', icon: X, label: 'Quitar de la playlist' }}
|
||||
rowAction={isOwner
|
||||
? { action: '?/removeTrack', icon: X, label: 'Quitar de la playlist' }
|
||||
: undefined}
|
||||
/>
|
||||
{:else}
|
||||
<EmptyState
|
||||
|
||||
@@ -299,14 +299,9 @@
|
||||
</Surface>
|
||||
|
||||
<Checkbox bind:checked={acceptedTerms}>
|
||||
Acepto los
|
||||
<a
|
||||
href="/terms"
|
||||
class="text-accent-soft underline underline-offset-2 transition hover:brightness-110"
|
||||
>
|
||||
términos y condiciones
|
||||
</a>
|
||||
al subir {title.trim() !== '' ? `«${title}»` : 'esta canción'}.
|
||||
Acepto los términos y condiciones al subir {title.trim() !== ''
|
||||
? `«${title}»`
|
||||
: 'esta canción'}.
|
||||
</Checkbox>
|
||||
|
||||
{#if status === 'uploading'}
|
||||
|
||||
Reference in New Issue
Block a user