Files
maher 703f50a09d
Some checks failed
Build / run (push) Has been cancelled
first commit
2025-10-29 11:42:25 +01:00

168 lines
5.5 KiB
TypeScript
Executable File

import React, {Fragment, useState} from 'react';
import {MainNavbar} from '@app/main-navbar';
import {useDarkThemeVariables} from '@common/ui/themes/use-dark-theme-variables';
import {
useWatchPageVideo,
UseWatchPageVideoResponse,
} from '@app/videos/requests/use-watch-page-video';
import {Footer} from '@common/ui/footer/footer';
import {PageErrorMessage} from '@common/errors/page-error-message';
import {CommentList} from '@common/comments/comment-list/comment-list';
import {NewCommentForm} from '@common/comments/new-comment-form';
import {WatchPageTitleDetails} from '@app/videos/watch-page/watch-page-title-details';
import {WatchPageAside} from '@app/videos/watch-page/watch-page-aside';
import {AnimatePresence, m} from 'framer-motion';
import {opacityAnimation} from '@common/ui/animation/opacity-animation';
import {useScrollToTop} from '@common/ui/navigation/use-scroll-to-top';
import {VideoPlayerSkeleton} from '@app/videos/video-player-skeleton';
import {SiteVideoPlayer} from '@app/videos/site-video-player';
import {useSettings} from '@common/core/settings/use-settings';
import {useAuth} from '@common/auth/use-auth';
import {useIsStreamingMode} from '@app/videos/use-is-streaming-mode';
import {WatchPageAlternativeVideos} from '@app/videos/watch-page/watch-page-alternative-videos';
import {AdHost} from '@common/admin/ads/ad-host';
import {Episode} from '@app/titles/models/episode';
import {Title} from '@app/titles/models/title';
import {Video} from '@app/titles/models/video';
import {PageMetaTags} from '@common/http/page-meta-tags';
import {useLayoutEffect} from '@react-aria/utils';
import {VideoThumbnail} from '@app/videos/video-thumbnail';
import {Trans} from '@common/i18n/trans';
import {Button} from '@common/ui/buttons/button';
import {Link} from 'react-router-dom';
export function WatchPage() {
const darkThemeVars = useDarkThemeVariables();
useScrollToTop();
return (
<Fragment>
<MainNavbar />
<div style={darkThemeVars} className="dark min-h-screen bg text">
<div className="container mx-auto p-14 md:p-24">
<Content />
<Footer className="mt-48" />
</div>
</div>
</Fragment>
);
}
function Content() {
const {titles, comments} = useSettings();
const {isLoggedIn, hasPermission} = useAuth();
const query = useWatchPageVideo();
const {data, isLoading} = query;
const title = data?.title;
const episode = data?.episode;
const video = data?.video;
let commentable: Episode | Title | Video | undefined = video;
if (!comments?.per_video) {
commentable = episode || title;
}
const shouldShowComments =
title && video && titles.enable_comments && hasPermission('comments.view');
if (data || isLoading) {
return (
<Fragment key={video?.id || 'loading'}>
<PageMetaTags query={query} />
<VideoWrapper data={data} />
<WatchPageAlternativeVideos data={data} />
<AdHost slot="watch_top" className="pt-48" />
<section className="mt-42 items-start gap-56 lg:flex">
<div className="flex-auto">
<WatchPageTitleDetails />
{shouldShowComments && (
<CommentList
commentable={commentable!}
className="mt-44"
perPage={20}
>
{isLoggedIn && hasPermission('comments.create') && (
<NewCommentForm
commentable={commentable!}
className="mb-14 mt-24"
/>
)}
</CommentList>
)}
</div>
<WatchPageAside />
</section>
</Fragment>
);
}
return <PageErrorMessage />;
}
interface VideoWrapperProps {
data?: UseWatchPageVideoResponse;
}
function VideoWrapper({data}: VideoWrapperProps) {
const isStreamingMode = useIsStreamingMode();
const {hasPermission} = useAuth();
const [isVisible, setIsVisible] = useState(false);
useLayoutEffect(() => {
setIsVisible(true);
}, []);
return (
<AnimatePresence initial={false} mode="wait">
{data?.video && isVisible ? (
<m.div key="player" {...opacityAnimation}>
{hasPermission('videos.play') ? (
<SiteVideoPlayer
title={data.title}
episode={data.episode}
video={data.video}
relatedVideos={data.related_videos}
autoPlay
logPlays
showEpisodeSelector={isStreamingMode}
/>
) : (
<UpgradeMessage video={data.video} />
)}
</m.div>
) : (
<m.div className="relative" key="skeleton" {...opacityAnimation}>
<VideoPlayerSkeleton animate />
</m.div>
)}
</AnimatePresence>
);
}
interface UpgradeMessageProps {
video: Video;
}
function UpgradeMessage({video}: UpgradeMessageProps) {
return (
<div className="relative flex aspect-video items-center justify-center bg-alt">
<div className="blur">
<VideoThumbnail video={video} />
</div>
<div className="absolute h-max w-max max-w-full rounded-lg bg-black/60 p-24 text-lg font-medium">
<div>
<Trans message="Your current plan does not allow watching videos. Upgrade to unlock this feature." />
</div>
<div className="mt-14 text-center">
<Button
variant="flat"
color="primary"
elementType={Link}
to="/pricing"
>
<Trans message="Upgrade" />
</Button>
</div>
</div>
</div>
);
}