150
resources/client/episodes/episode-page.tsx
Executable file
150
resources/client/episodes/episode-page.tsx
Executable file
@@ -0,0 +1,150 @@
|
||||
import {PageStatus} from '@common/http/page-status';
|
||||
import {Title} from '@app/titles/models/title';
|
||||
import {PageMetaTags} from '@common/http/page-meta-tags';
|
||||
import React, {Fragment} from 'react';
|
||||
import {TitlePageHeaderImage} from '@app/titles/pages/title-page/title-page-header-image';
|
||||
import {
|
||||
GetEpisodeResponse,
|
||||
useEpisode,
|
||||
} from '@app/episodes/requests/use-episode';
|
||||
import {ChipList} from '@common/ui/forms/input-field/chip-field/chip-list';
|
||||
import {Chip} from '@common/ui/forms/input-field/chip-field/chip';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {TitlePageCast} from '@app/titles/pages/title-page/sections/title-page-cast';
|
||||
import {RelatedTitlesPanel} from '@app/titles/related-titles-panel';
|
||||
import {CompactCredits} from '@app/titles/compact-credits';
|
||||
import {TitlePageAsideLayout} from '@app/titles/pages/title-page/title-page-aside-layout';
|
||||
import {WatchlistButton} from '@app/user-lists/watchlist-button';
|
||||
import {TitlePoster} from '@app/titles/title-poster/title-poster';
|
||||
import {getGenreLink} from '@app/titles/genre-link';
|
||||
import {SitePageLayout} from '@app/site-page-layout';
|
||||
import {EpisodePageHeader} from '@app/episodes/episode-page-header';
|
||||
import {TruncatedDescription} from '@common/ui/truncated-description';
|
||||
import {TitlePageVideoGrid} from '@app/titles/pages/title-page/sections/title-page-video-grid';
|
||||
import {useIsStreamingMode} from '@app/videos/use-is-streaming-mode';
|
||||
import {WatchNowButton} from '@app/titles/pages/title-page/watch-now-button';
|
||||
import {Episode} from '@app/titles/models/episode';
|
||||
import {TitlePageSections} from '@app/titles/pages/title-page/sections/title-page-sections';
|
||||
import {TitlePageEpisodeGrid} from '@app/titles/pages/title-page/sections/title-page-episode-grid';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {useSettings} from '@common/core/settings/use-settings';
|
||||
|
||||
export function EpisodePage() {
|
||||
const query = useEpisode('episodePage');
|
||||
|
||||
const content = query.data ? (
|
||||
<Fragment>
|
||||
<PageMetaTags query={query} />
|
||||
<PageContent data={query.data} />
|
||||
</Fragment>
|
||||
) : (
|
||||
<PageStatus query={query} loaderClassName="absolute inset-0 m-auto" />
|
||||
);
|
||||
|
||||
return <SitePageLayout>{content}</SitePageLayout>;
|
||||
}
|
||||
|
||||
interface PageContentProps {
|
||||
data: GetEpisodeResponse;
|
||||
}
|
||||
function PageContent({data}: PageContentProps) {
|
||||
const {episode, title} = data;
|
||||
return (
|
||||
<div>
|
||||
<TitlePageHeaderImage title={title} episode={episode} />
|
||||
<div className="container mx-auto mt-12 px-14 md:mt-40 md:px-24">
|
||||
<div className="items-start gap-54 md:flex">
|
||||
<Aside title={title} episode={episode} />
|
||||
<div className="flex-auto">
|
||||
<EpisodePageHeader title={title} episode={episode} />
|
||||
<MainContent data={data} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface MainContentProps {
|
||||
data: GetEpisodeResponse;
|
||||
}
|
||||
function MainContent({data}: MainContentProps) {
|
||||
const {episode, title, credits} = data;
|
||||
const {title_page} = useSettings();
|
||||
return (
|
||||
<main className="@container">
|
||||
{title.genres?.length ? (
|
||||
<ChipList>
|
||||
{title.genres.map(genre => (
|
||||
<Chip
|
||||
className="capitalize"
|
||||
elementType={Link}
|
||||
to={getGenreLink(genre)}
|
||||
key={genre.id}
|
||||
>
|
||||
{genre.display_name || genre.name}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipList>
|
||||
) : null}
|
||||
<TruncatedDescription
|
||||
className="mt-16"
|
||||
description={episode.description}
|
||||
/>
|
||||
<CompactCredits credits={credits} />
|
||||
{title_page?.sections.map(name => (
|
||||
<EpisodePageSection key={name} name={name} data={data} />
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
interface EpisodePageSectionProps {
|
||||
data: GetEpisodeResponse;
|
||||
name: (typeof TitlePageSections)[number];
|
||||
}
|
||||
function EpisodePageSection({name, data}: EpisodePageSectionProps) {
|
||||
switch (name) {
|
||||
case 'videos':
|
||||
return <TitlePageVideoGrid title={data.title} episode={data.episode} />;
|
||||
case 'cast':
|
||||
return <TitlePageCast credits={data.credits?.actors} />;
|
||||
case 'related':
|
||||
return <RelatedTitlesPanel title={data.title} />;
|
||||
case 'episodes':
|
||||
return (
|
||||
<TitlePageEpisodeGrid
|
||||
data={data}
|
||||
label={<Trans message="Other episodes" />}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface AsideProps {
|
||||
title: Title;
|
||||
episode: Episode;
|
||||
}
|
||||
function Aside({title, episode}: AsideProps) {
|
||||
const isStreamingMode = useIsStreamingMode();
|
||||
return (
|
||||
<TitlePageAsideLayout
|
||||
className="max-md:hidden"
|
||||
poster={<TitlePoster title={title} size="w-full" srcSize="lg" />}
|
||||
>
|
||||
{isStreamingMode && episode.primary_video && (
|
||||
<WatchNowButton
|
||||
video={episode.primary_video}
|
||||
variant="flat"
|
||||
defaultLabel
|
||||
/>
|
||||
)}
|
||||
<WatchlistButton
|
||||
item={title}
|
||||
variant={isStreamingMode ? 'outline' : 'flat'}
|
||||
/>
|
||||
</TitlePageAsideLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user