import {ChannelContentProps} from '@app/channels/channel-content'; import React, {Fragment} from 'react'; import {useCarousel} from '@app/channels/carousel/use-carousel'; import {Title} from '@app/titles/models/title'; import {TitleRating} from '@app/reviews/title-rating'; import {Button} from '@common/ui/buttons/button'; import {Trans} from '@common/i18n/trans'; import {MediaPlayIcon} from '@common/icons/media/media-play'; import {TitleLink} from '@app/titles/title-link'; import {TitleBackdrop} from '@app/titles/title-poster/title-backdrop'; import {TitlePoster} from '@app/titles/title-poster/title-poster'; import {IconButton} from '@common/ui/buttons/icon-button'; import {ChevronLeftIcon} from '@common/icons/material/ChevronLeft'; import {ChevronRightIcon} from '@common/icons/material/ChevronRight'; import {ChannelHeader} from '@app/channels/channel-header/channel-header'; import {AnimatePresence, m} from 'framer-motion'; import {Link} from 'react-router-dom'; import {getWatchLink} from '@app/videos/watch-page/get-watch-link'; import {useChannelContent} from '@common/channels/requests/use-channel-content'; import {Channel, ChannelContentItem} from '@common/channels/channel'; export function ChannelContentSlider({ channel, isNested, }: ChannelContentProps) { const { scrollContainerRef, activePage, canScrollBackward, canScrollForward, scrollToNextPage, scrollToPreviousPage, } = useCarousel({rotate: true}); const {data: pagination} = useChannelContent<ChannelContentItem<Title>>(channel); return ( <Fragment> <ChannelHeader channel={channel as Channel} isNested={isNested} margin="mb-18" /> <div className="gap-24 md:flex"> <div className="relative flex-auto"> <div ref={scrollContainerRef} className="hidden-scrollbar flex h-full select-none snap-x snap-mandatory snap-always items-center overflow-x-auto" > {pagination?.data.map((item, index) => ( <Slide key={item.id} item={item} index={index} /> ))} </div> <div className="absolute top-10 z-20 w-full md:top-[170px]"> <div className="absolute left-8 hidden md:left-14 md:block"> <IconButton variant="outline" size="lg" color="white" disabled={!canScrollBackward} onClick={() => scrollToPreviousPage()} > <ChevronLeftIcon /> </IconButton> </div> <div className="absolute right-8 hidden md:right-14 md:block"> <IconButton variant="outline" size="lg" color="white" disabled={!canScrollForward} onClick={() => scrollToNextPage()} > <ChevronRightIcon /> </IconButton> </div> </div> </div> <UpNext titles={pagination?.data ?? []} activePage={activePage} /> </div> </Fragment> ); } interface SlideProps { item: Title; index: number; } function Slide({item, index}: SlideProps) { return ( <div className="relative h-full w-full flex-shrink-0 snap-start snap-normal overflow-hidden rounded"> <TitleBackdrop title={item} lazy={index > 0} className="min-h-240 md:min-h-0" wrapperClassName="h-full" /> <div className="absolute inset-0 isolate flex h-full w-full items-center justify-start gap-24 rounded p-30 text-white md:items-end"> <div className="absolute left-0 h-full w-full bg-gradient-to-b from-black/40 max-md:top-0 md:bottom-0 md:h-3/4 md:bg-gradient-to-t md:from-black/100" /> <TitlePoster title={item} size="max-h-320" srcSize="md" className="z-10 shadow-md max-md:hidden" /> <div className="z-10 text-lg md:max-w-620"> <TitleRating score={item.rating} /> <div className="my-8 text-2xl md:text-5xl"> <TitleLink title={item} /> </div> {item.description && ( <p className="max-md:hidden">{item.description}</p> )} {item.primary_video && ( <Button variant="flat" color="primary" startIcon={<MediaPlayIcon />} radius="rounded-full" className="mt-24 md:min-h-42 md:min-w-144" elementType={Link} to={getWatchLink(item.primary_video)} > {item.primary_video.category === 'full' ? ( <Trans message="Watch now" /> ) : ( <Trans message="Play trailer" /> )} </Button> )} </div> </div> </div> ); } interface UpNextProps { titles: Title[]; activePage: number; } function UpNext({titles, activePage}: UpNextProps) { const itemCount = titles.length; const start = activePage + 1; const end = start + 3; const items = titles.slice(start, end); if (end > itemCount) { items.push(...titles.slice(0, end - itemCount)); } return ( <AnimatePresence initial={false} mode="wait"> <div className="w-1/4 max-w-200 flex-shrink-0 max-md:hidden"> <div className="mb-12 text-lg font-semibold"> <Trans message="Up next" /> </div> <div className="flex flex-col gap-24"> {items.map(item => ( <m.div key={item.id} className="relative flex-auto" initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}} transition={{duration: 0.2}} > <TitleBackdrop title={item} className="mb-6 rounded" size="w-full" srcSize="md" wrapWithLink showPlayButton /> <div className="mb-2 overflow-hidden overflow-ellipsis whitespace-nowrap text-sm"> <TitleLink title={item} className="text-base font-medium" /> </div> <div> <TitleRating score={item.rating} className="text-sm" /> </div> </m.div> ))} </div> </div> </AnimatePresence> ); }