138
resources/client/channels/channel-header/channel-header.tsx
Executable file
138
resources/client/channels/channel-header/channel-header.tsx
Executable file
@@ -0,0 +1,138 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user