feat(player): queue

This commit is contained in:
2024-11-27 01:54:32 +01:00
parent c93d47efc1
commit 60613e6502
14 changed files with 1492 additions and 1564 deletions

View File

@@ -55,14 +55,22 @@
console.log(`SKIP TO QUEUE INDEX ${index}`);
}
const submitTogglePause: SubmitFunction = async () => {
const submitPlayerAction: SubmitFunction = async () => {
return async ({ update, result }) => {
await update({
invalidateAll: false
});
if (result.type === 'success' && result.data && 'isPaused' in result.data) {
player.isPaused = result.data.isPaused;
if (result.type !== 'success' || !result.data) {
return;
}
if ('isPaused' in result.data) {
if (!('volume' in result.data)) {
player.isPaused = result.data.isPaused;
} else {
player.applyStatus(result.data);
}
}
};
};
@@ -93,9 +101,9 @@
<form
class="col-span-1 flex justify-center gap-1"
method="POST"
use:enhance={submitTogglePause}
use:enhance={submitPlayerAction}
>
<Button variant="outline" size="icon">
<Button variant="outline" size="icon" disabled>
<SkipBack />
</Button>
<Button type="submit" formaction="/player?/toggle-pause" variant="outline" size="icon">
@@ -105,7 +113,7 @@
<Pause />
{/if}
</Button>
<Button variant="outline" size="icon">
<Button type="submit" variant="outline" formaction="/player?/skip" size="icon">
<SkipForward />
</Button>
</form>
@@ -116,7 +124,7 @@
{#snippet child({ props })}
<Button {...props} variant="ghost" size="sm">
<List class="mr-2 size-4" />
<span>15</span>
<span>{player.queue.length}</span>
</Button>
{/snippet}
</Popover.Trigger>
@@ -125,34 +133,35 @@
<Separator class="my-1" />
<div class="flex h-full flex-col gap-1 overflow-y-auto pr-3">
{#each [] as _track, i}
<form
class="flex h-full flex-col gap-1 overflow-y-auto pr-3"
method="POST"
use:enhance={submitPlayerAction}
>
{#each player.queue as track, i}
<button
type="submit"
class="flex flex-row items-center gap-2 rounded-lg px-3 py-2 transition-all hover:bg-secondary"
onclick={() => skipToQueueIndex(i)}
formaction="/player?/skip-to-queue-index&index={i}"
>
<div class="min-w-8 overflow-hidden rounded-md">
<img
src="https://i.scdn.co/image/ab67616d0000b2732c0ead8ce0dd1c6e2fca817f"
class="aspect-square size-8"
alt="Cover"
/>
<img src={getCoverUrl(track.hash)} class="aspect-square size-8" alt="Cover" />
</div>
<div class="flex flex-col overflow-hidden">
<p
class="w-full self-start overflow-hidden text-ellipsis text-nowrap text-left text-sm text-foreground/80"
>
Track name
{track.name}
</p>
<p
class="w-full self-start overflow-hidden text-left text-xs text-muted-foreground"
>
Track artist
{track.artistName}
</p>
</div>
</button>
{/each}
</div>
</form>
</Popover.Content>
</Popover.Root>

View File

@@ -2,11 +2,10 @@
import { enhance } from '$app/forms';
import { getCoverUrl } from '$lib/covers';
import { getPlayerState } from '$lib/player.svelte';
// import { AudioLines } from 'lucide-svelte';
import { AudioLines } from 'lucide-svelte';
import { AudioLines, Music2, ListMusic, ListEnd } from 'lucide-svelte';
import type { SubmitFunction } from '../../../routes/tracks/[hash]/$types';
import type { Track } from '$lib/proto/library';
import * as ContextMenu from '$lib/components/ui/context-menu';
interface Props {
track: Track;
@@ -14,21 +13,44 @@
let { track }: Props = $props();
let isOpen = $state(false);
const player = getPlayerState();
const submitPlayTrack: SubmitFunction = async () => {
return async ({ update, result }) => {
isOpen = false;
await update({
invalidateAll: false
});
if (result.type === 'success' && result.data) {
if (result.type === 'success' && result.data && 'track' in result.data) {
player.queue = [];
player.currentlyPlaying = result.data.track ?? null;
player.progress = result.data.position;
player.isPaused = false;
}
};
};
const submitEnqueue: SubmitFunction = async () => {
return async ({ update, result }) => {
isOpen = false;
await update({
invalidateAll: false
});
if (result.type === 'success' && result.data && 'tracks' in result.data) {
player.queue = result.data.tracks;
}
};
};
function contextMenuItemClicked(e: MouseEvent) {
e.stopPropagation();
}
</script>
<form
@@ -37,24 +59,81 @@
action="/tracks/{track.hash}?/play"
use:enhance={submitPlayTrack}
>
<div class="relative">
<button type="submit" class="relative overflow-hidden rounded-lg">
<img
class="aspect-square w-auto transition-all duration-150 hover:scale-105 hover:saturate-150"
src={getCoverUrl(track.hash)}
alt={track.name}
/>
</button>
<ContextMenu.Root bind:open={isOpen}>
<ContextMenu.Trigger>
<div class="relative">
<button type="submit" class="relative overflow-hidden rounded-lg">
<img
class="aspect-square w-auto transition-all duration-150 hover:scale-105 hover:saturate-150"
src={getCoverUrl(track.hash)}
alt={track.name}
/>
</button>
<div
class="absolute bottom-6 left-2 size-4 animate-pulse"
class:hidden={player.currentlyPlaying?.hash !== track.hash}
>
<AudioLines class="text-primary" />
</div>
</div>
<div class="relative space-y-1 text-sm">
<h3 class="font-medium leading-none">{track.name}</h3>
<p class="text-xs text-muted-foreground">{track.artistName}</p>
</div>
<div
class="absolute bottom-6 left-2 size-4 animate-pulse"
class:hidden={player.currentlyPlaying?.hash !== track.hash}
>
<AudioLines class="text-primary" />
</div>
</div>
<div class="relative space-y-1 text-sm">
<h3 class="font-medium leading-none">{track.name}</h3>
<p class="text-xs text-muted-foreground">{track.artistName}</p>
</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item class="!p-0">
<form
class="flex w-full"
method="POST"
action="/tracks/{track.hash}?/play"
use:enhance={submitPlayTrack}
>
<button
type="submit"
class="flex w-full items-center px-2 py-1.5 focus:outline-0"
onclick={contextMenuItemClicked}
>
<Music2 class="mr-1 size-4" />
Play
</button>
</form>
</ContextMenu.Item>
<ContextMenu.Item class="!p-0">
<form
class="flex w-full"
method="POST"
action="/tracks/{track.hash}?/play-next"
use:enhance={submitEnqueue}
>
<button
type="submit"
class="flex w-full items-center px-2 py-1.5 focus:outline-0"
onclick={contextMenuItemClicked}
>
<ListMusic class="mr-1 size-4" />
Play Next
</button>
</form>
</ContextMenu.Item>
<ContextMenu.Item class="!p-0">
<form
class="flex w-full"
method="POST"
action="/tracks/{track.hash}?/add-to-queue"
use:enhance={submitEnqueue}
>
<button
type="submit"
class="flex w-full items-center px-2 py-1.5 focus:outline-0"
onclick={contextMenuItemClicked}
>
<ListEnd class="mr-1 size-4" />
Add to Queue
</button>
</form>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>
</form>