A grid of your albums with a create dialog, and a detail page with the tracklist, inline editing and a picker that lists the songs you uploaded so they can be dropped into the album. Covers reuse the playlist mosaic since an album has no artwork of its own yet.
93 lines
2.9 KiB
Svelte
93 lines
2.9 KiB
Svelte
<script lang="ts">
|
|
import { player, queueIdForTrack, toQueueItems } from '$lib/player/player.svelte';
|
|
import Page from '$lib/components/ui/Page.svelte';
|
|
import Modal from '$lib/components/ui/Modal.svelte';
|
|
import Alert from '$lib/components/ui/Alert.svelte';
|
|
import AlbumForm from '$lib/components/ui/AlbumForm.svelte';
|
|
import Button from '$lib/components/ui/Button.svelte';
|
|
import AlbumHeader from './components/AlbumHeader.svelte';
|
|
import AlbumTrackTable from './components/AlbumTrackTable.svelte';
|
|
import LibraryPicker from './components/LibraryPicker.svelte';
|
|
|
|
let { data, form } = $props();
|
|
|
|
const album = $derived(data.album);
|
|
const tracks = $derived(data.tracks);
|
|
const library = $derived(data.library);
|
|
const isOwner = $derived(data.isOwner);
|
|
|
|
let editing = $state(false);
|
|
let confirmingDelete = $state(false);
|
|
|
|
const isCurrentQueue = $derived(
|
|
tracks.some((track) => queueIdForTrack(track) === player.current.id)
|
|
);
|
|
|
|
function playAll() {
|
|
if (tracks.length === 0) return;
|
|
if (isCurrentQueue) player.toggle();
|
|
else player.playQueue(toQueueItems(tracks), 0);
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>{album.title} · Musify</title>
|
|
<meta name="description" content="Álbum {album.title} en Musify." />
|
|
</svelte:head>
|
|
|
|
<Page>
|
|
<AlbumHeader
|
|
id={album.id}
|
|
title={album.title}
|
|
description={album.description ?? undefined}
|
|
releaseYear={album.releaseYear === null ? undefined : Number(album.releaseYear)}
|
|
trackIds={tracks.map((track) => track.id)}
|
|
playing={isCurrentQueue && player.playing}
|
|
{isOwner}
|
|
onPlayAll={playAll}
|
|
onEdit={() => (editing = true)}
|
|
onDelete={() => (confirmingDelete = true)}
|
|
/>
|
|
|
|
{#if form?.message}
|
|
<Alert tone="danger" class="mt-6">{form.message}</Alert>
|
|
{/if}
|
|
|
|
{#if tracks.length > 0}
|
|
<AlbumTrackTable {tracks} {isOwner} />
|
|
{/if}
|
|
|
|
{#if isOwner && library.length > 0}
|
|
<LibraryPicker {library} />
|
|
{/if}
|
|
</Page>
|
|
|
|
<Modal open={editing} onClose={() => (editing = false)} title="Editar álbum">
|
|
<AlbumForm
|
|
action="?/edit"
|
|
initialTitle={album.title}
|
|
initialDescription={album.description ?? ''}
|
|
initialReleaseYear={album.releaseYear === null ? undefined : Number(album.releaseYear)}
|
|
formMessage={form?.message}
|
|
submitLabel="Guardar"
|
|
submittingLabel="Guardando…"
|
|
onCancel={() => (editing = false)}
|
|
onSuccess={() => (editing = false)}
|
|
/>
|
|
</Modal>
|
|
|
|
<Modal open={confirmingDelete} onClose={() => (confirmingDelete = false)} maxWidth="max-w-sm">
|
|
<h2 class="text-lg font-bold tracking-tight text-fg">
|
|
¿Eliminar «{album.title}»?
|
|
</h2>
|
|
<p class="mt-2 text-base text-fg-3">
|
|
Esta acción no se puede deshacer. Las canciones que contiene seguirán en tu biblioteca.
|
|
</p>
|
|
<div class="mt-6 flex items-center justify-end gap-3">
|
|
<Button variant="secondary" onclick={() => (confirmingDelete = false)}>Cancelar</Button>
|
|
<form method="POST" action="?/delete">
|
|
<Button type="submit" variant="danger">Eliminar</Button>
|
|
</form>
|
|
</div>
|
|
</Modal>
|