feat: Front page now streaming & upcoming, styling improvements
This commit is contained in:
@@ -46,7 +46,7 @@ export interface TmdbSeriesFull2 extends TmdbSeries2 {
|
||||
}
|
||||
|
||||
export class TmdbApi implements Api<paths> {
|
||||
getClient() {
|
||||
static getClient() {
|
||||
return createClient<paths>({
|
||||
baseUrl: 'https://api.themoviedb.org',
|
||||
headers: {
|
||||
@@ -55,6 +55,10 @@ export class TmdbApi implements Api<paths> {
|
||||
});
|
||||
}
|
||||
|
||||
getClient() {
|
||||
return TmdbApi.getClient();
|
||||
}
|
||||
|
||||
// MOVIES
|
||||
|
||||
getTmdbMovie = async (tmdbId: number) => {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<div class={classNames('flex flex-col group/carousel', $$restProps.class)}>
|
||||
<div class={'flex justify-between items-center mb-2 ' + scrollClass}>
|
||||
<div class="font-medium tracking-wide text-2xl text-zinc-200">
|
||||
<div class="header2">
|
||||
<slot name="header" />
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -53,8 +53,12 @@
|
||||
style={`left: ${x}px; top: ${y}px; width: ${width}px; height: ${height}px;`}
|
||||
/>
|
||||
|
||||
<div class="fixed inset-0 border-x-[96px] border-y-[48px] border-green-500/10 z-50" />
|
||||
<div class="fixed inset-0 px-32 grid grid-cols-12 gap-x-16 *:bg-purple-500/10 items-stretch z-50">
|
||||
<div
|
||||
class="fixed inset-0 border-x-[96px] border-y-[48px] border-green-500/10 z-50 pointer-events-none"
|
||||
/>
|
||||
<div
|
||||
class="fixed inset-0 px-32 grid grid-cols-12 gap-x-16 *:bg-purple-500/10 items-stretch z-50 pointer-events-none"
|
||||
>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
|
||||
@@ -1,27 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { Bookmark, CardStack, Gear, Laptop, MagnifyingGlass } from 'radix-icons-svelte';
|
||||
import {
|
||||
Bookmark,
|
||||
CardStack,
|
||||
DotFilled,
|
||||
Gear,
|
||||
Laptop,
|
||||
MagnifyingGlass
|
||||
} from 'radix-icons-svelte';
|
||||
import classNames from 'classnames';
|
||||
import { type Readable, writable, type Writable } from 'svelte/store';
|
||||
import Container from '../../../Container.svelte';
|
||||
import { useNavigate } from 'svelte-navigator';
|
||||
import { registrars, type Selectable } from '../../selectable';
|
||||
import { useLocation, useNavigate } from 'svelte-navigator';
|
||||
import { registrars, Selectable } from '../../selectable';
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
let selectedIndex = 0;
|
||||
$: activeIndex = {
|
||||
'': 0,
|
||||
series: 0,
|
||||
movies: 1,
|
||||
library: 2,
|
||||
search: 3,
|
||||
manage: 4
|
||||
}[$location.pathname.split('/')[1] || '/'];
|
||||
|
||||
$: console.log('activeIndex', activeIndex);
|
||||
$: console.log($location.pathname.split('/')[1] || '/');
|
||||
let isNavBarOpen: Readable<boolean>;
|
||||
let focusIndex: Writable<number> = writable(0);
|
||||
let selectable: Selectable;
|
||||
|
||||
focusIndex.subscribe((v) => (selectedIndex = v));
|
||||
|
||||
const itemContainer = (index: number, _focusIndex: number) =>
|
||||
classNames('h-12 flex items-center cursor-pointer', {
|
||||
'text-primary-500': _focusIndex === index,
|
||||
'text-stone-300': _focusIndex !== index
|
||||
});
|
||||
|
||||
const selectIndex = (index: number) => () => {
|
||||
if (index === activeIndex) {
|
||||
Selectable.giveFocus('right');
|
||||
return;
|
||||
}
|
||||
selectable.focusChild(index);
|
||||
const path =
|
||||
{
|
||||
@@ -39,7 +55,7 @@
|
||||
<Container
|
||||
class={classNames(
|
||||
'flex flex-col items-stretch fixed z-20 left-0 inset-y-0 group',
|
||||
'py-4 w-16 select-none',
|
||||
'py-8 w-24 select-none',
|
||||
{
|
||||
//'max-w-[64px]': !$isNavBarOpen,
|
||||
//'max-w-64': $isNavBarOpen
|
||||
@@ -77,10 +93,16 @@
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 0)
|
||||
})}
|
||||
>
|
||||
<div class="absolute inset-y-0 left-2 flex items-center justify-center">
|
||||
<DotFilled
|
||||
class={classNames('text-primary-500', { 'opacity-0': activeIndex !== 0 })}
|
||||
size={19}
|
||||
/>
|
||||
</div>
|
||||
<Laptop class="w-8 h-8" />
|
||||
<span
|
||||
class={classNames(
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-16',
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-20',
|
||||
{
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
@@ -99,10 +121,16 @@
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 1)
|
||||
})}
|
||||
>
|
||||
<div class="absolute inset-y-0 left-2 flex items-center justify-center">
|
||||
<DotFilled
|
||||
class={classNames('text-primary-500', { 'opacity-0': activeIndex !== 1 })}
|
||||
size={19}
|
||||
/>
|
||||
</div>
|
||||
<CardStack class="w-8 h-8" />
|
||||
<span
|
||||
class={classNames(
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-16',
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-20',
|
||||
{
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
@@ -121,10 +149,16 @@
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 2)
|
||||
})}
|
||||
>
|
||||
<div class="absolute inset-y-0 left-2 flex items-center justify-center">
|
||||
<DotFilled
|
||||
class={classNames('text-primary-500', { 'opacity-0': activeIndex !== 2 })}
|
||||
size={19}
|
||||
/>
|
||||
</div>
|
||||
<Bookmark class="w-8 h-8" />
|
||||
<span
|
||||
class={classNames(
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-16',
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-20',
|
||||
{
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
@@ -143,10 +177,16 @@
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 3)
|
||||
})}
|
||||
>
|
||||
<div class="absolute inset-y-0 left-2 flex items-center justify-center">
|
||||
<DotFilled
|
||||
class={classNames('text-primary-500', { 'opacity-0': activeIndex !== 3 })}
|
||||
size={19}
|
||||
/>
|
||||
</div>
|
||||
<MagnifyingGlass class="w-8 h-8" />
|
||||
<span
|
||||
class={classNames(
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-16',
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-20',
|
||||
{
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
@@ -161,16 +201,27 @@
|
||||
|
||||
<Container class="w-full h-12 cursor-pointer" on:clickOrSelect={selectIndex(4)} let:hasFocus>
|
||||
<div
|
||||
class={classNames('w-full h-full relative flex items-center justify-center', {
|
||||
'text-primary-500': hasFocus || (!$isNavBarOpen && selectedIndex === 4),
|
||||
'text-stone-300 hover:text-primary-500':
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 4)
|
||||
})}
|
||||
class={classNames(
|
||||
'w-full h-full relative flex items-center justify-center transition-opacity',
|
||||
{
|
||||
'text-primary-500': hasFocus || (!$isNavBarOpen && selectedIndex === 4),
|
||||
'text-stone-300 hover:text-primary-500':
|
||||
!hasFocus && !(!$isNavBarOpen && selectedIndex === 4),
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div class="absolute inset-y-0 left-2 flex items-center justify-center">
|
||||
<DotFilled
|
||||
class={classNames('text-primary-500', { 'opacity-0': activeIndex !== 4 })}
|
||||
size={19}
|
||||
/>
|
||||
</div>
|
||||
<Gear class="w-8 h-8" />
|
||||
<span
|
||||
class={classNames(
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-16',
|
||||
'text-xl font-medium transition-opacity flex items-center absolute inset-y-0 left-20',
|
||||
{
|
||||
'opacity-0 pointer-events-none': $isNavBarOpen === false,
|
||||
'group-hover:opacity-100 group-hover:pointer-events-auto': true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Container from '../../Container.svelte';
|
||||
import HeroShowcase from '../components/HeroShowcase/HeroShowcase.svelte';
|
||||
import { tmdbApi } from '../apis/tmdb/tmdb-api';
|
||||
import { TmdbApi, tmdbApi } from '../apis/tmdb/tmdb-api';
|
||||
import { getShowcasePropsFromTmdbMovie } from '../components/HeroShowcase/HeroShowcase';
|
||||
import Carousel from '../components/Carousel/Carousel.svelte';
|
||||
import CarouselPlaceholderItems from '../components/Carousel/CarouselPlaceholderItems.svelte';
|
||||
@@ -11,53 +11,98 @@
|
||||
import JellyfinCard from '../components/Card/JellyfinCard.svelte';
|
||||
import { Route } from 'svelte-navigator';
|
||||
import MoviePage from './MoviePage.svelte';
|
||||
import { formatDateToYearMonthDay } from '../utils';
|
||||
import TmdbCard from '../components/Card/TmdbCard.svelte';
|
||||
|
||||
const { data: continueWatching, isLoading: isLoadingContinueWatching } = useRequest(
|
||||
jellyfinApi.getContinueWatching,
|
||||
'movie'
|
||||
);
|
||||
const { data: recentlyAdded, isLoading: isLoadingRecentlyAdded } = useRequest(
|
||||
jellyfinApi.getRecentlyAdded,
|
||||
'movie'
|
||||
);
|
||||
const continueWatching = jellyfinApi.getContinueWatching('movie');
|
||||
const recentlyAdded = jellyfinApi.getRecentlyAdded('movie');
|
||||
|
||||
const popularMovies = tmdbApi.getPopularMovies();
|
||||
|
||||
const newDigitalReleases = getDigitalReleases();
|
||||
const upcomingMovies = getUpcomingMovies();
|
||||
|
||||
function getUpcomingMovies() {
|
||||
return TmdbApi.getClient()
|
||||
.GET('/3/discover/movie', {
|
||||
params: {
|
||||
query: {
|
||||
'primary_release_date.gte': formatDateToYearMonthDay(new Date()),
|
||||
sort_by: 'popularity.desc'
|
||||
// language: $settings.language,
|
||||
// region: $settings.discover.region,
|
||||
// with_original_language: parseIncludedLanguages($settings.discover.includedLanguages)
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((res) => res.data?.results || []);
|
||||
}
|
||||
|
||||
function getDigitalReleases() {
|
||||
return TmdbApi.getClient()
|
||||
.GET('/3/discover/movie', {
|
||||
params: {
|
||||
query: {
|
||||
with_release_type: 4,
|
||||
sort_by: 'popularity.desc',
|
||||
'release_date.lte': formatDateToYearMonthDay(new Date())
|
||||
// language: $settings.language,
|
||||
// with_original_language: parseIncludedLanguages($settings.discover.includedLanguages)
|
||||
// region: $settings.discover.region
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((res) => res.data?.results || []);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container focusOnMount class="flex flex-col">
|
||||
<div class="h-[calc(100vh-12rem)] flex px-20">
|
||||
<div class="h-[calc(100vh-12rem)] flex px-32">
|
||||
<HeroShowcase
|
||||
items={popularMovies.then(getShowcasePropsFromTmdbMovie)}
|
||||
on:enter={scrollIntoView({ top: 0 })}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-16">
|
||||
<Carousel scrollClass="px-20" on:enter={scrollIntoView({ vertical: 64 })}>
|
||||
<div slot="header">
|
||||
{$isLoadingContinueWatching || ($isLoadingRecentlyAdded && !$continueWatching?.length)
|
||||
? 'Loading...'
|
||||
: $continueWatching?.length
|
||||
? 'Continue Watching'
|
||||
: 'Recently Added'}
|
||||
</div>
|
||||
{#if $isLoadingContinueWatching || ($isLoadingRecentlyAdded && !$continueWatching?.length)}
|
||||
<CarouselPlaceholderItems />
|
||||
{:else if $continueWatching?.length}
|
||||
<div class="flex -mx-4">
|
||||
{#each $continueWatching as item (item.Id)}
|
||||
<Container class="m-4" on:enter={scrollIntoView({ horizontal: 64 + 20 })}>
|
||||
<JellyfinCard size="lg" {item} />
|
||||
</Container>
|
||||
<div class="my-16 space-y-8">
|
||||
{#await continueWatching then continueWatching}
|
||||
{#if continueWatching?.length}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Continue Watching</span>
|
||||
{#each continueWatching as item (item.Id)}
|
||||
<JellyfinCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else if $recentlyAdded?.length}
|
||||
{#each $recentlyAdded as item (item.Id)}
|
||||
<Container on:enter={scrollIntoView({ horizontal: 64 + 20 })}>
|
||||
<JellyfinCard size="lg" {item} />
|
||||
</Container>
|
||||
{/each}
|
||||
</Carousel>
|
||||
{:else}
|
||||
{#await recentlyAdded then recentlyAdded}
|
||||
{#if recentlyAdded?.length}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Recently Added</span>
|
||||
{#each recentlyAdded as item (item.Id)}
|
||||
<JellyfinCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</Carousel>
|
||||
{/await}
|
||||
|
||||
{#await newDigitalReleases then nowStreaming}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">New Digital Releases</span>
|
||||
{#each nowStreaming as item}
|
||||
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/await}
|
||||
|
||||
{#await upcomingMovies then upcomingSeries}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Upcoming Movies</span>
|
||||
{#each upcomingSeries as item}
|
||||
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/await}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Container from '../../Container.svelte';
|
||||
import { tmdbApi } from '../apis/tmdb/tmdb-api';
|
||||
import { TmdbApi, tmdbApi } from '../apis/tmdb/tmdb-api';
|
||||
|
||||
import { jellyfinApi } from '../apis/jellyfin/jellyfin-api';
|
||||
import { useRequest } from '../stores/data.store';
|
||||
@@ -12,102 +12,94 @@
|
||||
import JellyfinCard from '../components/Card/JellyfinCard.svelte';
|
||||
import { Route } from 'svelte-navigator';
|
||||
import SeriesPage from '../components/SeriesPage/SeriesPage.svelte';
|
||||
import { formatDateToYearMonthDay } from '../utils';
|
||||
import TmdbCard from '../components/Card/TmdbCard.svelte';
|
||||
|
||||
const { data: continueWatching, isLoading: isLoadingContinueWatching } = useRequest(
|
||||
jellyfinApi.getContinueWatchingSeries
|
||||
);
|
||||
const { data: recentlyAdded, isLoading: isLoadingRecentlyAdded } = useRequest(
|
||||
jellyfinApi.getRecentlyAdded,
|
||||
'series'
|
||||
);
|
||||
const continueWatching = jellyfinApi.getContinueWatchingSeries();
|
||||
const recentlyAdded = jellyfinApi.getRecentlyAdded('series');
|
||||
|
||||
// const jellyfinItemsPromise = new Promise<JellyfinItem[]>((resolve) => {
|
||||
// jellyfinItemsStore.subscribe((data) => {
|
||||
// if (data.loading) return;
|
||||
// resolve(data.data || []);
|
||||
// });
|
||||
// });
|
||||
const nowStreaming = getNowStreaming();
|
||||
const upcomingSeries = fetchUpcomingSeries();
|
||||
|
||||
// const fetchCardProps = async (
|
||||
// items: {
|
||||
// name?: string;
|
||||
// title?: string;
|
||||
// id?: number;
|
||||
// vote_average?: number;
|
||||
// number_of_seasons?: number;
|
||||
// first_air_date?: string;
|
||||
// poster_path?: string;
|
||||
// }[],
|
||||
// type: TitleType | undefined = undefined
|
||||
// ): Promise<ComponentProps<Card>[]> => {
|
||||
// const filtered = $settings.discover.excludeLibraryItems
|
||||
// ? items.filter(
|
||||
// async (item) =>
|
||||
// !(await jellyfinItemsPromise).find((i) => i.ProviderIds?.Tmdb === String(item.id))
|
||||
// )
|
||||
// : items;
|
||||
//
|
||||
// return Promise.all(filtered.map(async (item) => getPosterProps(item, type))).then((props) =>
|
||||
// props.filter((p) => p.backdropUrl).map((i) => ({ ...i, openInModal: false }))
|
||||
// );
|
||||
// };
|
||||
//
|
||||
// const fetchNowStreaming = () =>
|
||||
// TmdbApiOpen.GET('/3/discover/tv', {
|
||||
// params: {
|
||||
// query: {
|
||||
// 'air_date.gte': formatDateToYearMonthDay(new Date()),
|
||||
// 'first_air_date.lte': formatDateToYearMonthDay(new Date()),
|
||||
// sort_by: 'popularity.desc',
|
||||
// language: $settings.language,
|
||||
// with_original_language: parseIncludedLanguages($settings.discover.includedLanguages)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .then((res) => res.data?.results || [])
|
||||
// .then((i) => fetchCardProps(i, 'series'));
|
||||
//
|
||||
// function parseIncludedLanguages(includedLanguages: string) {
|
||||
// return includedLanguages.replace(' ', '').split(',').join('|');
|
||||
// }
|
||||
function getNowStreaming() {
|
||||
return TmdbApi.getClient()
|
||||
.GET('/3/discover/tv', {
|
||||
params: {
|
||||
query: {
|
||||
'air_date.gte': formatDateToYearMonthDay(new Date()),
|
||||
'first_air_date.lte': formatDateToYearMonthDay(new Date()),
|
||||
sort_by: 'popularity.desc'
|
||||
// language: $settings.language,
|
||||
// with_original_language: parseIncludedLanguages($settings.discover.includedLanguages)
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((res) => res.data?.results || []);
|
||||
}
|
||||
|
||||
function fetchUpcomingSeries() {
|
||||
return TmdbApi.getClient()
|
||||
.GET('/3/discover/tv', {
|
||||
params: {
|
||||
query: {
|
||||
'first_air_date.gte': formatDateToYearMonthDay(new Date()),
|
||||
sort_by: 'popularity.desc'
|
||||
// language: $settings.language,
|
||||
// with_original_language: parseIncludedLanguages($settings.discover.includedLanguages)
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((res) => res.data?.results || []);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container focusOnMount class="flex flex-col">
|
||||
<div class="h-[calc(100vh-12rem)] flex px-20">
|
||||
<div class="h-[calc(100vh-12rem)] flex px-32">
|
||||
<HeroShowcase
|
||||
items={tmdbApi.getPopularSeries().then(getShowcasePropsFromTmdbSeries)}
|
||||
on:enter={scrollIntoView({ top: 0 })}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-16">
|
||||
<Carousel scrollClass="px-20" on:enter={scrollIntoView({ vertical: 64 })}>
|
||||
<div class="text-xl font-semibold text-zinc-300" slot="header">
|
||||
{$isLoadingContinueWatching || ($isLoadingRecentlyAdded && !$continueWatching?.length)
|
||||
? 'Loading...'
|
||||
: $continueWatching?.length
|
||||
? 'Continue Watching'
|
||||
: 'Recently Added'}
|
||||
</div>
|
||||
{#if $isLoadingContinueWatching || ($isLoadingRecentlyAdded && !$continueWatching?.length)}
|
||||
<CarouselPlaceholderItems />
|
||||
{:else if $continueWatching?.length}
|
||||
<div class="flex -mx-2">
|
||||
{#each $continueWatching as item (item.Id)}
|
||||
<Container class="m-4" on:enter={scrollIntoView({ horizontal: 64 + 20 })}>
|
||||
<JellyfinCard size="lg" {item} />
|
||||
</Container>
|
||||
<div class="my-16 space-y-8">
|
||||
{#await continueWatching then continueWatching}
|
||||
{#if continueWatching?.length}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Continue Watching</span>
|
||||
{#each continueWatching as item (item.Id)}
|
||||
<JellyfinCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else if $recentlyAdded?.length}
|
||||
<div class="flex -mx-4">
|
||||
{#each $recentlyAdded as item (item.Id)}
|
||||
<Container class="m-4" on:enter={scrollIntoView({ horizontal: 64 + 20 })}>
|
||||
<JellyfinCard size="lg" {item} />
|
||||
</Container>
|
||||
{/each}
|
||||
</div>
|
||||
</Carousel>
|
||||
{:else}
|
||||
{#await recentlyAdded then recentlyAdded}
|
||||
{#if recentlyAdded?.length}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Recently Added</span>
|
||||
{#each recentlyAdded as item (item.Id)}
|
||||
<JellyfinCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</Carousel>
|
||||
{/await}
|
||||
|
||||
{#await nowStreaming then nowStreaming}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Now Streaming</span>
|
||||
{#each nowStreaming as item}
|
||||
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/await}
|
||||
|
||||
{#await upcomingSeries then upcomingSeries}
|
||||
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
|
||||
<span slot="header">Upcoming Series</span>
|
||||
{#each upcomingSeries as item}
|
||||
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/await}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user