Add various new components
This commit is contained in:
parent
e162c674bf
commit
ff396e8ca2
6 changed files with 185 additions and 0 deletions
84
src/lib/components/custom/Views/AlbumView.svelte
Normal file
84
src/lib/components/custom/Views/AlbumView.svelte
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {AlbumViews} from "$lib/components/custom/Views/views.svelte";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
import LoadingSpinner from "$lib/components/custom/LoadingSpinner.svelte";
|
||||||
|
import ViewCard from "$lib/components/custom/Views/ViewCard.svelte";
|
||||||
|
import {type AlbumID3, type GetAlbumList2Response, OpenSubsonic, type Parameter} from "$lib/opensubsonic";
|
||||||
|
import {Skeleton} from "$lib/components/ui/skeleton";
|
||||||
|
import {goto} from "$app/navigation";
|
||||||
|
|
||||||
|
let { viewMode = $bindable() }: { viewMode: AlbumViews } = $props();
|
||||||
|
let previousView = $state(viewMode);
|
||||||
|
let albums: Array<AlbumID3> = $state([]);
|
||||||
|
let loading = $state(true);
|
||||||
|
let paginating = $state(false);
|
||||||
|
const paginationIncrement = 100; // TODO: make configurable?
|
||||||
|
let pagination = $state(0);
|
||||||
|
let self = $state();
|
||||||
|
let scrollY = $state(0);
|
||||||
|
let scrollYMax = $state(1);
|
||||||
|
|
||||||
|
async function fetchAlbums() {
|
||||||
|
let parameters: Array<Parameter> = [
|
||||||
|
{key: "type", value: viewMode},
|
||||||
|
{key: "size", value: paginationIncrement},
|
||||||
|
{key: "offset", value: pagination},
|
||||||
|
];
|
||||||
|
|
||||||
|
const data: GetAlbumList2Response = await OpenSubsonic.get("getAlbumList2", parameters);
|
||||||
|
if (data && data.albumList2.album) {
|
||||||
|
albums = albums.concat(data.albumList2.album);
|
||||||
|
pagination += paginationIncrement;
|
||||||
|
paginating = false;
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScroll(self) {
|
||||||
|
scrollY = self.scrollTop;
|
||||||
|
scrollYMax = self.scrollTopMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if(scrollY/scrollYMax && albums.length === 100 && !paginating) {
|
||||||
|
console.log("triggered")
|
||||||
|
paginating = true;
|
||||||
|
fetchAlbums();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
console.log(viewMode);
|
||||||
|
if (viewMode !== previousView) {
|
||||||
|
loading = true;
|
||||||
|
albums = [];
|
||||||
|
pagination = 0;
|
||||||
|
fetchAlbums();
|
||||||
|
previousView = viewMode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function selectAlbum(albumId) {
|
||||||
|
goto(`/album/${albumId}`);
|
||||||
|
console.log(albumId);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
fetchAlbums();
|
||||||
|
previousView = viewMode;
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="border border-2 flex flex-row flex-wrap gap-2 p-2 h-1 min-h-full items-center justify-center overflow-y-auto" bind:this={self} onscroll={() => updateScroll(self)}>
|
||||||
|
{#if loading}
|
||||||
|
{#each [...Array(20).keys()] as i}
|
||||||
|
<Skeleton id={"album-skeleton-" + i} class="rounded-md w-56 h-72 border border-2 bg-background" />
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#each albums as album}
|
||||||
|
<ViewCard data={album} onselectalbum={selectAlbum}/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
24
src/lib/components/custom/Views/PlaylistView.svelte
Normal file
24
src/lib/components/custom/Views/PlaylistView.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Separator} from "$lib/components/ui/separator";
|
||||||
|
import {Button} from "$lib/components/ui/button";
|
||||||
|
import {AlbumViews} from "$lib/components/custom/Views/views.svelte";
|
||||||
|
|
||||||
|
let { viewMode = $bindable() }: { viewMode: AlbumViews } = $props();
|
||||||
|
let range = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="border border-2 flex flex-col gap-1 p-2 h-1 min-h-full">
|
||||||
|
<h1>{viewMode}</h1>
|
||||||
|
<h1>View Actions go here</h1>
|
||||||
|
<div class="overflow-y-auto">
|
||||||
|
<p>everything above this should be sticky</p>
|
||||||
|
<h1>and this lists the songs</h1>
|
||||||
|
{#each range as ignore}
|
||||||
|
<p>A</p>
|
||||||
|
{/each}
|
||||||
|
<p>And this should be after infiny loading</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
42
src/lib/components/custom/Views/ViewCard.svelte
Normal file
42
src/lib/components/custom/Views/ViewCard.svelte
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Button} from "$lib/components/ui/button";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
import {type AlbumID3, type GetAlbumList2Response, OpenSubsonic, type Parameter} from "$lib/opensubsonic";
|
||||||
|
import {Skeleton} from "$lib/components/ui/skeleton";
|
||||||
|
|
||||||
|
let { data = $bindable(), onselectalbum }: { data: AlbumID3 } = $props();
|
||||||
|
let coverImage = $state(new Image());
|
||||||
|
let loading = $state(true);
|
||||||
|
|
||||||
|
async function getCoverImage() {
|
||||||
|
let parameters: Array<Parameter> = [
|
||||||
|
{key: "id", value: data.coverArt},
|
||||||
|
//{key: "size", value: paginationIncrement},
|
||||||
|
];
|
||||||
|
|
||||||
|
const imgData = await OpenSubsonic.get("getCoverArt", parameters);
|
||||||
|
if (data) {
|
||||||
|
coverImage.src = imgData.url;
|
||||||
|
coverImage.onload = () => {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
getCoverImage();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="border border-2 flex flex-col gap-1 p-2 h-72 w-56 rounded-md" onclick={() => onselectalbum(data.id)}>
|
||||||
|
{#if loading}
|
||||||
|
<Skeleton class="min-h-[192px] w-[192px]" />
|
||||||
|
{:else}
|
||||||
|
<img alt={data.name + " Cover Art"} src={coverImage.src} height="192px" width="192px" class="rounded-md"/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<h1>{data.name}</h1>
|
||||||
|
<h2>{data.artist}</h2>
|
||||||
|
</div>
|
17
src/lib/components/custom/Views/views.svelte.ts
Normal file
17
src/lib/components/custom/Views/views.svelte.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
export enum AlbumViews {
|
||||||
|
All = "alphabeticalByName",
|
||||||
|
Random = "random",
|
||||||
|
Favourites = "starred",
|
||||||
|
TopRated = "highest",
|
||||||
|
RecentlyAdded = "newest",
|
||||||
|
RecentlyPlayed = "recent",
|
||||||
|
MostPlayed = "frequent"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Views {
|
||||||
|
Albums = "Albums",
|
||||||
|
Artists = "Artists",
|
||||||
|
Songs = "Songs",
|
||||||
|
Radios = "Radios",
|
||||||
|
Shares = "Shares"
|
||||||
|
}
|
7
src/lib/components/ui/skeleton/index.ts
Normal file
7
src/lib/components/ui/skeleton/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Root from "./skeleton.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
//
|
||||||
|
Root as Skeleton,
|
||||||
|
};
|
11
src/lib/components/ui/skeleton/skeleton.svelte
Normal file
11
src/lib/components/ui/skeleton/skeleton.svelte
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("animate-pulse rounded-md bg-primary/10", className)} {...$$restProps}></div>
|
Loading…
Reference in a new issue