feat: add loading bar component and integrate it into TV shows and recommendations loading states and cache metadataprovider responsens in backend

This commit is contained in:
maxDorninger
2025-05-25 14:36:50 +02:00
parent e9578cbeaf
commit 304ff6b42d
13 changed files with 104 additions and 38 deletions

View File

@@ -6,6 +6,7 @@
import {goto} from '$app/navigation';
import {base} from '$app/paths';
import type {MetaDataProviderShowSearchResult} from '$lib/types.js';
import {toOptimizedURL} from "sveltekit-image-optimize/components";
let loading = $state(false);
let errorMessage = $state(null);
@@ -47,7 +48,7 @@
{#if result.poster_path != null}
<img
class="max-h-full max-w-full object-contain rounded-lg"
src={result.poster_path}
src={toOptimizedURL(result.poster_path)}
alt="{result.name}'s Poster Image"
/>
{:else}

View File

@@ -1,9 +1,15 @@
<script lang="ts" module>
import {Settings, LifeBuoy, Send, LayoutPanelLeft, TvIcon} from "lucide-svelte";
import {Settings, LifeBuoy, Send, LayoutPanelLeft, TvIcon, Home} from "lucide-svelte";
import {PUBLIC_VERSION} from '$env/static/public';
const data = {
navMain: [
{
title: 'Dashboard',
url: '/dashboard',
icon: Home,
isActive: true
},
{
title: 'TV',
url: '/dashboard/tv',
@@ -23,6 +29,18 @@
url: '/dashboard/tv/requests'
}
]
},
{
title: 'Settings',
url: '/dashboard/settings',
icon: Settings,
isActive: true,
items: [
{
title: 'Users',
url: '/dashboard/settings#users'
}
]
}
],
navSecondary: [
@@ -36,18 +54,6 @@
url: '#',
icon: Send
}
],
projects: [
{
name: 'Dashboard',
url: '/dashboard',
icon: LayoutPanelLeft
},
{
name: 'Settings',
url: '/dashboard/settings',
icon: Settings
}
]
};
</script>
@@ -85,7 +91,7 @@
</Sidebar.Header>
<Sidebar.Content>
<NavMain items={data.navMain}/>
<NavProjects projects={data.projects}/>
<!-- <NavProjects projects={data.projects}/> -->
<NavSecondary class="mt-auto" items={data.navSecondary}/>
</Sidebar.Content>
<Sidebar.Footer>

View File

@@ -0,0 +1,21 @@
<script lang="ts">
import {Progress} from "$lib/components/ui/progress/index.js";
import {onMount} from "svelte";
let value = $state(0);
onMount(() => {
const interval = setInterval(() => {
value += 1;
if (value >= 100) {
value = 0
clearInterval(interval);
}
}, 1);
return () => clearInterval(interval);
});
</script>
<Progress value={value}/>

View File

@@ -22,7 +22,7 @@
</script>
<Sidebar.Group>
<Sidebar.GroupLabel>Platform</Sidebar.GroupLabel>
<Sidebar.GroupLabel></Sidebar.GroupLabel>
<Sidebar.Menu>
{#each items as mainItem (mainItem.title)}
<Collapsible.Root open={mainItem.isActive}>

View File

@@ -23,7 +23,7 @@
</script>
<Sidebar.Group class="group-data-[collapsible=icon]:hidden">
<Sidebar.GroupLabel>Projects</Sidebar.GroupLabel>
<Sidebar.GroupLabel><!-- TODO: what to set this label to?? --> </Sidebar.GroupLabel>
<Sidebar.Menu>
{#each projects as item (item.name)}
<Sidebar.MenuItem>

View File

@@ -0,0 +1,7 @@
import Root from "./progress.svelte";
export {
Root,
//
Root as Progress,
};

View File

@@ -0,0 +1,24 @@
<script lang="ts">
import {Progress as ProgressPrimitive, type WithoutChildrenOrChild} from "bits-ui";
import {cn} from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
max = 100,
value,
...restProps
}: WithoutChildrenOrChild<ProgressPrimitive.RootProps> = $props();
</script>
<ProgressPrimitive.Root
{...restProps}
bind:ref
class={cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className)}
{value}
>
<div
class="bg-primary h-full w-full flex-1 transition-all"
style={`transform: translateX(-${100 - (100 * (value ?? 0)) / (max ?? 1)}%)`}
></div>
</ProgressPrimitive.Root>

View File

@@ -3,10 +3,13 @@
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import RecommendedShowsCarousel from '$lib/components/recommended-shows-carousel.svelte';
import LoadingBar from '$lib/components/loading-bar.svelte';
import {base} from '$app/paths';
import {page} from '$app/state';
import type {MetaDataProviderShowSearchResult} from "$lib/types";
let recommendedShows: Promise<MetaDataProviderShowSearchResult[]> = page.data.tvRecommendations;
let recommendedShows = page.data.tvRecommendations;
</script>
<header class="flex h-16 shrink-0 items-center gap-2">
@@ -30,7 +33,12 @@
<div class="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min items-center justify-center p-4">
<div class="xl:max-w-[1200px] lg:max-w-[750px] md:max-w-[500px] sm:max-w-[200px] mx-auto">
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight text-center my-4">Trending Shows</h3>
<RecommendedShowsCarousel shows={recommendedShows}/>
{#await recommendedShows}
<LoadingBar/>
{:then recommendations}
<RecommendedShowsCarousel shows={recommendations}/>
{/await}
</div>
</div>

View File

@@ -14,16 +14,5 @@ export const load: PageLoad = async ({fetch}) => {
credentials: 'include'
});
if (!response.ok) {
console.error(`Failed to fetch TV recommendations: ${response.statusText}`);
toast.error(`Failed to fetch TV recommendations: ${response.statusText}`);
return {tvRecommendations: []};
} else {
console.log('Fetched TV recommendations successfully');
toast.success('Fetched TV recommendations successfully');
}
const recommendations = await response.json();
console.log('Tv Recommendations:', recommendations);
return {tvRecommendations: recommendations};
return {tvRecommendations: await response.json()};
};

View File

@@ -10,7 +10,7 @@
<h1 class="scroll-m-20 my-6 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
Settings
</h1>
<Card.Root>
<Card.Root id="users">
<Card.Header>
<Card.Title>Users</Card.Title>
<Card.Description>Edit or delete users</Card.Description>

View File

@@ -8,6 +8,7 @@
import {toOptimizedURL} from 'sveltekit-image-optimize/components';
import {getFullyQualifiedShowName} from '$lib/utils';
import logo from '$lib/images/svelte-logo.svg';
import LoadingBar from '$lib/components/loading-bar.svelte';
let tvShowsPromise = page.data.tvShows;
</script>
@@ -33,19 +34,24 @@
</Breadcrumb.Root>
</div>
</header>
{#snippet loadingbar()}
<div class="flex flex-col items-center justify-center w-full col-span-full py-16 animate-fade-in">
<div class="w-1/2 max-w-xs">
<LoadingBar/>
</div>
</div>
{/snippet}
<div class="flex w-full flex-1 flex-col gap-4 p-4 pt-0">
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
TV Shows
</h1>
<div
class="grid w-full auto-rows-min gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
>
<div class="grid w-full auto-rows-min gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{#await tvShowsPromise}
Loading...
{@render loadingbar()}
{:then tvShowsJson}
{#await tvShowsJson.json()}
Loading...
{@render loadingbar()}
{:then tvShows}
{#each tvShows as show}
<a href={'/dashboard/tv/' + show.id}>