139 lines
4.3 KiB
TypeScript
Executable File
139 lines
4.3 KiB
TypeScript
Executable File
import React, {Fragment, ReactNode} from 'react';
|
|
import clsx from 'clsx';
|
|
import {Channel} from '@common/channels/channel';
|
|
import {FilterList} from '@common/datatable/filters/filter-list/filter-list';
|
|
import {AnimatePresence, m} from 'framer-motion';
|
|
import {opacityAnimation} from '@common/ui/animation/opacity-animation';
|
|
import {useBackendFilterUrlParams} from '@common/datatable/filters/backend-filter-url-params';
|
|
import {MOVIE_MODEL, SERIES_MODEL, TITLE_MODEL} from '@app/titles/models/title';
|
|
import {ChannelSortButton} from '@app/channels/channel-header/channel-sort-button';
|
|
import {AddFilterButton} from '@common/datatable/filters/add-filter-button';
|
|
import {TuneIcon} from '@common/icons/material/Tune';
|
|
import {SiteSectionHeading} from '@app/titles/site-section-heading';
|
|
import {Trans} from '@common/i18n/trans';
|
|
import {useParams} from 'react-router-dom';
|
|
import {ChannelLayoutButton} from '@app/channels/channel-header/channel-layout-button';
|
|
import {useTitleIndexFilters} from '@app/titles/use-title-index-filters';
|
|
import {FilterListSkeleton} from '@common/datatable/filters/filter-list/filter-list-skeleton';
|
|
import {UserListByline} from '@app/user-lists/user-list-byline';
|
|
import {UserListDetails} from '@app/user-lists/user-list-details';
|
|
|
|
const FilterModelTypes = [TITLE_MODEL, MOVIE_MODEL, SERIES_MODEL];
|
|
|
|
interface Props {
|
|
channel: Channel;
|
|
margin?: string;
|
|
isNested: boolean;
|
|
actions?: ReactNode;
|
|
}
|
|
export function ChannelHeader({
|
|
channel,
|
|
isNested,
|
|
actions,
|
|
margin = isNested ? 'mb-16 md:mb-30' : 'mb-20 md:mb-40',
|
|
}: Props) {
|
|
const shouldShowFilterButton =
|
|
!isNested &&
|
|
FilterModelTypes.includes(channel.config.contentModel) &&
|
|
channel.config.contentType === 'listAll';
|
|
|
|
const {encodedFilters} = useBackendFilterUrlParams();
|
|
const {filters, filtersLoading} = useTitleIndexFilters({
|
|
disabled: !shouldShowFilterButton,
|
|
});
|
|
|
|
if (channel.config.hideTitle) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<section className={clsx(margin)}>
|
|
<ChannelTitle
|
|
channel={channel}
|
|
isNested={isNested}
|
|
actions={
|
|
<Fragment>
|
|
{actions}
|
|
{!isNested && <ChannelSortButton channel={channel} />}
|
|
{shouldShowFilterButton && (
|
|
<AddFilterButton
|
|
icon={<TuneIcon />}
|
|
color={null}
|
|
variant="text"
|
|
disabled={filtersLoading}
|
|
filters={filters}
|
|
/>
|
|
)}
|
|
{!isNested && <ChannelLayoutButton channel={channel} />}
|
|
</Fragment>
|
|
}
|
|
/>
|
|
{shouldShowFilterButton && (
|
|
<div className="mt-14">
|
|
<AnimatePresence initial={false} mode="wait">
|
|
{filtersLoading && encodedFilters ? (
|
|
<FilterListSkeleton />
|
|
) : (
|
|
<m.div key="filter-list" {...opacityAnimation}>
|
|
<FilterList filters={filters} />
|
|
</m.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
interface ChannelTitleProps {
|
|
channel: Channel;
|
|
isNested: boolean;
|
|
actions?: ReactNode;
|
|
}
|
|
function ChannelTitle({channel, isNested, actions}: ChannelTitleProps) {
|
|
const {restriction: urlParam} = useParams();
|
|
if (channel.config.hideTitle) {
|
|
return null;
|
|
}
|
|
|
|
const link =
|
|
channel.config.restriction && urlParam
|
|
? `/${channel.slug}/${urlParam}`
|
|
: `/${channel.slug}`;
|
|
|
|
return (
|
|
<SiteSectionHeading
|
|
className="flex-auto"
|
|
margin="m-0"
|
|
description={<ChannelDescription channel={channel} />}
|
|
actions={actions}
|
|
headingType={isNested ? 'h2' : 'h1'}
|
|
descriptionFontSize={isNested ? 'text-sm' : undefined}
|
|
fontWeight={isNested ? 'font-normal' : undefined}
|
|
link={isNested ? link : undefined}
|
|
>
|
|
<Trans message={channel.name} />
|
|
</SiteSectionHeading>
|
|
);
|
|
}
|
|
|
|
interface ChannelDescriptionProps {
|
|
channel: Channel;
|
|
}
|
|
function ChannelDescription({channel}: ChannelDescriptionProps) {
|
|
if (channel.type === 'channel') {
|
|
return <Fragment>{channel.description}</Fragment>;
|
|
}
|
|
|
|
return (
|
|
<div className="mt-18 items-center text-sm md:flex">
|
|
{channel.user && <UserListByline user={channel.user} />}
|
|
<UserListDetails
|
|
list={channel}
|
|
className="ml-auto max-md:mt-14"
|
|
showShareButton
|
|
/>
|
|
</div>
|
|
);
|
|
}
|