From 2772abcd2f7976c1894fa8efee217b1dc2a99f7c Mon Sep 17 00:00:00 2001 From: 409 Date: Sun, 1 Dec 2024 03:49:08 +0100 Subject: [PATCH] feat!: playlists --- bun.lockb | Bin 97790 -> 97776 bytes package.json | 2 +- protos/library.proto | 37 ++ protos/player.proto | 10 + src/lib/components/groove/AppSidebar.svelte | 31 +- .../groove/CreatePlaylistDialog.svelte | 69 +++ src/lib/components/groove/Footer.svelte | 4 - .../components/groove/PlaylistListing.svelte | 115 +++++ src/lib/components/groove/Queue.svelte | 62 ++- .../ui/accordion/accordion-content.svelte | 24 + .../ui/accordion/accordion-item.svelte | 12 + .../ui/accordion/accordion-trigger.svelte | 31 ++ src/lib/components/ui/accordion/index.ts | 17 + .../components/ui/card/card-content.svelte | 16 + .../ui/card/card-description.svelte | 16 + src/lib/components/ui/card/card-footer.svelte | 16 + src/lib/components/ui/card/card-header.svelte | 16 + src/lib/components/ui/card/card-title.svelte | 25 + src/lib/components/ui/card/card.svelte | 20 + src/lib/components/ui/card/index.ts | 22 + .../ui/dialog/dialog-content.svelte | 38 ++ .../ui/dialog/dialog-description.svelte | 16 + .../components/ui/dialog/dialog-footer.svelte | 20 + .../components/ui/dialog/dialog-header.svelte | 20 + .../ui/dialog/dialog-overlay.svelte | 19 + .../components/ui/dialog/dialog-title.svelte | 16 + src/lib/components/ui/dialog/index.ts | 37 ++ src/lib/library.svelte.ts | 24 + src/lib/proto/library.client.ts | 61 +++ src/lib/proto/library.ts | 446 +++++++++++++++++- src/lib/proto/player.client.ts | 30 +- src/lib/proto/player.ts | 114 +++++ src/routes/+layout.server.ts | 36 +- src/routes/+layout.svelte | 13 +- src/routes/playlists/+page.server.ts | 24 + src/routes/playlists/+page.svelte | 15 +- src/routes/playlists/[id]/+page.server.ts | 19 + .../[id]/add-track/[hash]/+page.server.ts | 14 + src/routes/tracks/+page.server.ts | 21 - src/routes/tracks/+page.svelte | 71 ++- 40 files changed, 1530 insertions(+), 69 deletions(-) create mode 100644 src/lib/components/groove/CreatePlaylistDialog.svelte create mode 100644 src/lib/components/groove/PlaylistListing.svelte create mode 100644 src/lib/components/ui/accordion/accordion-content.svelte create mode 100644 src/lib/components/ui/accordion/accordion-item.svelte create mode 100644 src/lib/components/ui/accordion/accordion-trigger.svelte create mode 100644 src/lib/components/ui/accordion/index.ts create mode 100644 src/lib/components/ui/card/card-content.svelte create mode 100644 src/lib/components/ui/card/card-description.svelte create mode 100644 src/lib/components/ui/card/card-footer.svelte create mode 100644 src/lib/components/ui/card/card-header.svelte create mode 100644 src/lib/components/ui/card/card-title.svelte create mode 100644 src/lib/components/ui/card/card.svelte create mode 100644 src/lib/components/ui/card/index.ts create mode 100644 src/lib/components/ui/dialog/dialog-content.svelte create mode 100644 src/lib/components/ui/dialog/dialog-description.svelte create mode 100644 src/lib/components/ui/dialog/dialog-footer.svelte create mode 100644 src/lib/components/ui/dialog/dialog-header.svelte create mode 100644 src/lib/components/ui/dialog/dialog-overlay.svelte create mode 100644 src/lib/components/ui/dialog/dialog-title.svelte create mode 100644 src/lib/components/ui/dialog/index.ts create mode 100644 src/lib/library.svelte.ts create mode 100644 src/routes/playlists/[id]/+page.server.ts create mode 100644 src/routes/playlists/[id]/add-track/[hash]/+page.server.ts delete mode 100644 src/routes/tracks/+page.server.ts diff --git a/bun.lockb b/bun.lockb index 3d79beb82f025131bbc2932ea86539b56dfbbcac..e6d5434d22daf3a49b4bb2d39345869e6f705a06 100755 GIT binary patch delta 8539 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 DAbm_k delta 8560 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 import { page } from '$app/stores'; import * as Sidebar from '$lib/components/ui/sidebar'; - import { Home, Library, ListMusic, Music2, Settings } from 'lucide-svelte'; + import Home from 'virtual:icons/lucide/home'; + import Library from 'virtual:icons/lucide/library'; + import ListMusic from 'virtual:icons/lucide/list-music'; + import Music2 from 'virtual:icons/lucide/music-2'; + import Settings from 'virtual:icons/lucide/settings'; + import CirclePlus from 'virtual:icons/lucide/circle-plus'; + import { cn } from '$lib/utils'; + import CreatePlaylistDialog from './CreatePlaylistDialog.svelte'; + import { getLibraryState } from '$lib/library.svelte'; + + const library = getLibraryState(); @@ -64,6 +74,25 @@ {/snippet} + + + + {#snippet child({ props })} + + + Create Playlist + + {/snippet} + + + {#each library.playlists as playlist} + + + {playlist.name} + + + {/each} + diff --git a/src/lib/components/groove/CreatePlaylistDialog.svelte b/src/lib/components/groove/CreatePlaylistDialog.svelte new file mode 100644 index 0000000..8c9297a --- /dev/null +++ b/src/lib/components/groove/CreatePlaylistDialog.svelte @@ -0,0 +1,69 @@ + + + + + {@render children?.()} + + + + Name your Playlist + Make it memorable + + +
+ + +
+
+
diff --git a/src/lib/components/groove/Footer.svelte b/src/lib/components/groove/Footer.svelte index 319dedd..d873a4d 100644 --- a/src/lib/components/groove/Footer.svelte +++ b/src/lib/components/groove/Footer.svelte @@ -52,10 +52,6 @@ }; }; - async function skipToQueueIndex(index: number) { - console.log(`SKIP TO QUEUE INDEX ${index}`); - } - const submitPlayerAction: SubmitFunction = async () => { return async ({ update, result }) => { await update({ diff --git a/src/lib/components/groove/PlaylistListing.svelte b/src/lib/components/groove/PlaylistListing.svelte new file mode 100644 index 0000000..9d4da20 --- /dev/null +++ b/src/lib/components/groove/PlaylistListing.svelte @@ -0,0 +1,115 @@ + + +
+
1}> + {#each coverImages as cover} + + {:else} +
+ {/each} +
+
+
+

{name}

+
+

+ {dayjs(totalDuration, 'milliseconds').format('mm:ss')} +

+

+ {tracks.length} track{tracks.length !== 1 ? 's' : ''} +

+
+
+
+
+ +
+ +
+ +
+
+
+
diff --git a/src/lib/components/groove/Queue.svelte b/src/lib/components/groove/Queue.svelte index 9781dc2..1f7bf4c 100644 --- a/src/lib/components/groove/Queue.svelte +++ b/src/lib/components/groove/Queue.svelte @@ -2,9 +2,9 @@ import { deserialize, enhance } from '$app/forms'; import { getCoverUrl } from '$lib/covers'; import { getPlayerState } from '$lib/player.svelte'; - import { flip } from 'svelte/animate'; import type { SubmitFunction } from '../../../routes/player/$types'; import type { Track } from '$lib/proto/library'; + import { ScrollArea } from '$lib/components/ui/scroll-area'; const player = getPlayerState(); @@ -63,37 +63,35 @@ } -
- {#each player.queue as track, i} - - {/each} + + + {#each player.queue as track, i} + + {/each} +