feat: Simple people pages implementation

This commit is contained in:
Aleksi Lassila
2024-05-30 16:28:05 +03:00
parent 2a376bb0ef
commit 12edf92754
6 changed files with 141 additions and 10 deletions

View File

@@ -242,6 +242,34 @@ export class TmdbApi implements Api<paths> {
}) })
.then((res) => res.data?.results || []) || Promise.resolve([]); .then((res) => res.data?.results || []) || Promise.resolve([]);
getPerson = async (person_id: number) =>
this.getClient()
.GET('/3/person/{person_id}', {
params: {
path: {
person_id: person_id
},
query: {
append_to_response: 'images,movie_credits,tv_credits,external_ids'
}
}
})
.then((res) => res.data as TmdbPersonFull);
getPersonTaggedImages = async (person_id: number) =>
this.getClient()
?.GET('/3/person/{person_id}/tagged_images', {
params: {
path: {
person_id: person_id
}
}
})
.then((res) => res.data?.results || []) || Promise.resolve([]);
getPersonBackdrops = async (person_id: number) =>
this.getPersonTaggedImages(person_id).then((r) => r.filter((i) => (i.aspect_ratio || 0) > 1.5));
// OTHER // OTHER
// USER // USER

View File

@@ -40,7 +40,7 @@
</script> </script>
<Container <Container
class={classNames('fixed inset-0 z-20 bg-secondary-800 overflow-y-auto scrollbar-hide', { class={classNames('fixed inset-0 z-20 bg-secondary-900 overflow-y-auto scrollbar-hide', {
hidden: !topmost hidden: !topmost
})} })}
trapFocus trapFocus

View File

@@ -3,6 +3,7 @@
import Container from '../../../Container.svelte'; import Container from '../../../Container.svelte';
import AnimateScale from '../AnimateScale.svelte'; import AnimateScale from '../AnimateScale.svelte';
import type { Readable } from 'svelte/store'; import type { Readable } from 'svelte/store';
import { navigate } from '../StackRouter/StackRouter';
export let tmdbId: number; export let tmdbId: number;
// export let type: TitleType = 'person'; // export let type: TitleType = 'person';
@@ -17,23 +18,19 @@
<AnimateScale hasFocus={$hasFocus}> <AnimateScale hasFocus={$hasFocus}>
<Container <Container
class={classNames( class={classNames(
'flex flex-col justify-start rounded-xl overflow-hidden relative shadow-lg shrink-0 selectable hover:text-inherit hover:bg-stone-800 focus-visible:bg-stone-800 bg-secondary-800 group text-left', 'flex flex-col justify-start rounded-xl overflow-hidden relative shadow-lg shrink-0 selectable hover:text-inherit hover:bg-stone-800 focus-visible:bg-stone-800 bg-secondary-800 group text-left cursor-pointer',
{ {
'w-56 h-80': size === 'md', 'w-56 h-80': size === 'md',
'h-52': size === 'lg', 'h-52': size === 'lg',
'w-full': size === 'dynamic' 'w-full': size === 'dynamic'
} }
)} )}
on:clickOrSelect on:clickOrSelect={() => {
on:click={() => { if (tmdbId) navigate(`/person/${tmdbId}`);
// if (openInModal) {
// openTitleModal({ type, id: tmdbId, provider: 'tmdb' });
// } else {
// window.location.href = `/${type}/${tmdbId}`;
// }
}} }}
on:enter on:enter
bind:hasFocus bind:hasFocus
focusOnClick
> >
<!-- <div--> <!-- <div-->
<!-- class="mx-auto rounded-full overflow-hidden flex-shrink-0 aspect-square w-full bg-zinc-200 bg-opacity-20"--> <!-- class="mx-auto rounded-full overflow-hidden flex-shrink-0 aspect-square w-full bg-zinc-200 bg-opacity-20"-->

View File

@@ -14,7 +14,7 @@
<PersonCard <PersonCard
tmdbId={tmdbCredit.id || -1} tmdbId={tmdbCredit.id || -1}
name={tmdbCredit.original_name || 'Unknown'} name={tmdbCredit.name || 'Unknown'}
{subtitle} {subtitle}
backdropUrl={TMDB_PROFILE_LARGE + tmdbCredit.profile_path} backdropUrl={TMDB_PROFILE_LARGE + tmdbCredit.profile_path}
on:clickOrSelect on:clickOrSelect

View File

@@ -10,6 +10,7 @@ import LibraryPage from '../../pages/LibraryPage.svelte';
import SearchPage from '../../pages/SearchPage.svelte'; import SearchPage from '../../pages/SearchPage.svelte';
import PageNotFound from '../../pages/PageNotFound.svelte'; import PageNotFound from '../../pages/PageNotFound.svelte';
import ManagePage from '../../pages/ManagePage.svelte'; import ManagePage from '../../pages/ManagePage.svelte';
import PersonPage from '../../pages/PersonPage.svelte';
interface Page { interface Page {
id: symbol; id: symbol;
@@ -21,7 +22,11 @@ interface Route {
path: string; path: string;
component: ComponentType; component: ComponentType;
default?: boolean; default?: boolean;
// When root is navigated to, stcak is cleared
root?: boolean; root?: boolean;
// Parent route, that is always rendered under this route,
// possibly sharing props that are a subset of the child's props.
// Child's props are also passed to these.
parent?: Route; parent?: Route;
} }
@@ -205,6 +210,11 @@ const movieRoute: Route = {
parent: moviesHomeRoute parent: moviesHomeRoute
}; };
const personRoute: Route = {
path: '/person/:id',
component: PersonPage
};
const libraryRoute: Route = { const libraryRoute: Route = {
path: '/library', path: '/library',
component: LibraryPage, component: LibraryPage,
@@ -236,6 +246,7 @@ export const defaultStackRouter = useStackRouter({
episodeRoute, episodeRoute,
moviesHomeRoute, moviesHomeRoute,
movieRoute, movieRoute,
personRoute,
libraryRoute, libraryRoute,
searchRoute, searchRoute,
manageRoute manageRoute

View File

@@ -0,0 +1,95 @@
<script lang="ts">
import { TMDB_POSTER_SMALL } from '../constants.js';
import { tmdbApi } from '../apis/tmdb/tmdb-api';
import DetachedPage from '../components/DetachedPage/DetachedPage.svelte';
import classNames from 'classnames';
import { DotFilled } from 'radix-icons-svelte';
import CardGrid from '../components/CardGrid.svelte';
import TmdbCard from '../components/Card/TmdbCard.svelte';
import Container from '../../Container.svelte';
import { scrollIntoView } from '../selectable';
export let id: string;
$: person = tmdbApi.getPerson(Number(id));
$: titles = person.then((person) => {
if (person.known_for_department === 'Acting') {
return [...(person.movie_credits.cast || []), ...(person.tv_credits.cast || [])].sort(
(a, b) =>
// @ts-ignore
(b.release_date ?? b.first_air_date ?? 0) > (a.release_date ?? a.first_air_date ?? 0)
? 1
: -1
);
} else {
return [...(person.movie_credits.crew || []), ...(person.tv_credits.crew || [])].sort(
(a, b) =>
// @ts-ignore
(b.release_date ?? b.first_air_date ?? 0) > (a.release_date ?? a.first_air_date ?? 0)
? 1
: -1
);
}
});
</script>
<DetachedPage let:handleGoBack let:registrar>
{#await person then person}
<Container
focusOnMount
on:back={handleGoBack}
on:mount={registrar}
class="px-32 py-16 space-y-16"
>
<div class="flex space-x-8">
<div
class="bg-center bg-cover rounded-xl w-44 h-64 cursor-pointer"
style={`background-image: url("${TMDB_POSTER_SMALL + person.profile_path}")`}
/>
<div class="flex flex-col justify-end">
<div
class={classNames(
'text-left font-medium tracking-wider text-stone-200 hover:text-amber-200 mt-2',
{
'text-4xl sm:text-5xl 2xl:text-6xl': person.name?.length || 0 < 15,
'text-3xl sm:text-4xl 2xl:text-5xl': person.name?.length || 0 >= 15
}
)}
>
{person.name}
</div>
<div
class="flex items-center gap-1 uppercase text-zinc-300 font-semibold tracking-wider mt-2 text-lg"
>
<p class="flex-shrink-0">
Born {new Date(person.birthday || 0).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
<!-- <DotFilled />
<p class="flex-shrink-0">{movie.runtime}</p> -->
<DotFilled />
<p class="flex-shrink-0">
<a href={'https://www.themoviedb.org/person/' + id}>
{(person.movie_credits.cast?.length || 0) + (person.tv_credits.cast?.length || 0)} Credits</a
>
</p>
</div>
<div class="text-stone-300 font-medium line-clamp-3 opacity-75 max-w-4xl mt-4 text-lg">
{person.biography}
</div>
</div>
</div>
<CardGrid>
{#await titles then titles}
{#each titles as title}
<TmdbCard item={title} on:enter={scrollIntoView({ vertical: 128 })} />
{/each}
{/await}
</CardGrid>
</Container>
{/await}
</DetachedPage>