feat: Simple people pages implementation
This commit is contained in:
@@ -242,6 +242,34 @@ export class TmdbApi implements Api<paths> {
|
||||
})
|
||||
.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
|
||||
|
||||
// USER
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
<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
|
||||
})}
|
||||
trapFocus
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Container from '../../../Container.svelte';
|
||||
import AnimateScale from '../AnimateScale.svelte';
|
||||
import type { Readable } from 'svelte/store';
|
||||
import { navigate } from '../StackRouter/StackRouter';
|
||||
|
||||
export let tmdbId: number;
|
||||
// export let type: TitleType = 'person';
|
||||
@@ -17,23 +18,19 @@
|
||||
<AnimateScale hasFocus={$hasFocus}>
|
||||
<Container
|
||||
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',
|
||||
'h-52': size === 'lg',
|
||||
'w-full': size === 'dynamic'
|
||||
}
|
||||
)}
|
||||
on:clickOrSelect
|
||||
on:click={() => {
|
||||
// if (openInModal) {
|
||||
// openTitleModal({ type, id: tmdbId, provider: 'tmdb' });
|
||||
// } else {
|
||||
// window.location.href = `/${type}/${tmdbId}`;
|
||||
// }
|
||||
on:clickOrSelect={() => {
|
||||
if (tmdbId) navigate(`/person/${tmdbId}`);
|
||||
}}
|
||||
on:enter
|
||||
bind:hasFocus
|
||||
focusOnClick
|
||||
>
|
||||
<!-- <div-->
|
||||
<!-- class="mx-auto rounded-full overflow-hidden flex-shrink-0 aspect-square w-full bg-zinc-200 bg-opacity-20"-->
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<PersonCard
|
||||
tmdbId={tmdbCredit.id || -1}
|
||||
name={tmdbCredit.original_name || 'Unknown'}
|
||||
name={tmdbCredit.name || 'Unknown'}
|
||||
{subtitle}
|
||||
backdropUrl={TMDB_PROFILE_LARGE + tmdbCredit.profile_path}
|
||||
on:clickOrSelect
|
||||
|
||||
@@ -10,6 +10,7 @@ import LibraryPage from '../../pages/LibraryPage.svelte';
|
||||
import SearchPage from '../../pages/SearchPage.svelte';
|
||||
import PageNotFound from '../../pages/PageNotFound.svelte';
|
||||
import ManagePage from '../../pages/ManagePage.svelte';
|
||||
import PersonPage from '../../pages/PersonPage.svelte';
|
||||
|
||||
interface Page {
|
||||
id: symbol;
|
||||
@@ -21,7 +22,11 @@ interface Route {
|
||||
path: string;
|
||||
component: ComponentType;
|
||||
default?: boolean;
|
||||
// When root is navigated to, stcak is cleared
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -205,6 +210,11 @@ const movieRoute: Route = {
|
||||
parent: moviesHomeRoute
|
||||
};
|
||||
|
||||
const personRoute: Route = {
|
||||
path: '/person/:id',
|
||||
component: PersonPage
|
||||
};
|
||||
|
||||
const libraryRoute: Route = {
|
||||
path: '/library',
|
||||
component: LibraryPage,
|
||||
@@ -236,6 +246,7 @@ export const defaultStackRouter = useStackRouter({
|
||||
episodeRoute,
|
||||
moviesHomeRoute,
|
||||
movieRoute,
|
||||
personRoute,
|
||||
libraryRoute,
|
||||
searchRoute,
|
||||
manageRoute
|
||||
|
||||
95
src/lib/pages/PersonPage.svelte
Normal file
95
src/lib/pages/PersonPage.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user