Advertisement
justfrenzy

CrackFlix - frontend /src/components/App/MediaModal.tsx

May 12th, 2024
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 12.62 KB | Source Code | 0 0
  1. import React, { useContext, useEffect, useState } from "react";
  2. import {
  3.     Modal,
  4.     ModalContent,
  5.     ModalHeader,
  6.     ModalBody,
  7.     ModalFooter,
  8.     Button,
  9.     Card,
  10.     Skeleton,
  11.     Divider,
  12.     Image,
  13. } from "@nextui-org/react";
  14. import { MediaPreview, MediaTrailer, ToggleLikedFunc } from "./handleFunctions";
  15. import { Play, PlusCircle, StarIcon } from "lucide-react";
  16. import { AppContext, AppContextTypes } from "../../utils/AppContext";
  17. import { useNavigate } from "react-router-dom";
  18. import { ResponseType, mediaModalProps, mediaType } from "./props.interface";
  19. import TVModalText from "./MediaModalText";
  20. import circleCheck from "../../assets/circle-check.svg";
  21. import toast from "react-hot-toast";
  22. import Cookies from "js-cookie";
  23.  
  24. type MediaModalProps = {
  25.     mediaId: number;
  26.     isMoviesMedia: boolean;
  27.     isOpen: boolean;
  28.     onOpenChange: () => void;
  29. };
  30.  
  31. const MediaModal: React.FC<MediaModalProps> = ({
  32.     mediaId,
  33.     isMoviesMedia,
  34.     isOpen,
  35.     onOpenChange,
  36. }) => {
  37.     const navigate = useNavigate();
  38.     const [trailer, setTrailer] = useState<string>(""); // State for trailer
  39.     const { defaultLanguage, accessToken, onOpen, theme, systemTheme } =
  40.         useContext<AppContextTypes>(AppContext); // Context for app
  41.     const [mediaDetails, setMediaDetails] = useState<mediaType>({} as mediaType); // State for media details
  42.     const [isLoading, setIsLoading] = useState<boolean>(false); // State for loading status
  43.     const [runtime, setRuntime] = useState<number>(0); // State for runtime
  44.     const [checked, setChecked] = useState(false); // State for checkbox
  45.     const { min, country, genre, closeBtn, playBtn } =
  46.         defaultLanguage === "en-US"
  47.             ? (TVModalText.en as mediaModalProps["en"])
  48.             : (TVModalText.bg as mediaModalProps["bg"]);
  49.  
  50.     useEffect(() => {
  51.         if (isOpen) {
  52.             setIsLoading(true);
  53.             const waitDetails = async () => {
  54.                 const details = async () => {
  55.                     const token = Cookies.get(`access-token`);
  56.                     const response = (await MediaPreview(
  57.                         mediaId as number,
  58.                         defaultLanguage as AppContextTypes["defaultLanguage"],
  59.                         isMoviesMedia as boolean,
  60.                         token as string
  61.                     )) as ResponseType;
  62.                     const response2 = (await MediaTrailer(
  63.                         mediaId as number,
  64.                         isMoviesMedia as boolean
  65.                     )) as response2Fetch;
  66.  
  67.                     type response2Fetch = {
  68.                         trailer: Array<{
  69.                             name: string;
  70.                             key: string;
  71.                         }>;
  72.                     };
  73.  
  74.                     setMediaDetails(response.details);
  75.                     setChecked(response.details.check);
  76.                     setTrailer(
  77.                         response2.trailer.find((trail) => trail.name.includes("Trailer"))
  78.                             ?.key || ""
  79.                     );
  80.                     setRuntime(
  81.                         isMoviesMedia
  82.                             ? response.details.runtime
  83.                             : response.details.episode_run_time[0]
  84.                     );
  85.                 };
  86.  
  87.                 await details(); // Fetch media details and trailer
  88.             };
  89.  
  90.             waitDetails(); // Wait for media details and trailer
  91.         } else {
  92.             setMediaDetails({} as mediaType);
  93.             setTrailer("");
  94.         }
  95.  
  96.         setIsLoading(false); // Set loading state to false
  97.     }, [isOpen, mediaId, defaultLanguage, isMoviesMedia]);
  98.  
  99.     if (!mediaId || !mediaDetails) {
  100.         return null;
  101.     }
  102.     /**
  103.      * Handles the release date format
  104.      * @param {string} date - The release date
  105.      * @returns {string} - The formatted release year
  106.      */
  107.     const handleReleaseDate = (date: string): string => date?.split("-")[0] || "";
  108.  
  109.     const handleToggleLiked = async () => {
  110.         const mediaType = isMoviesMedia ? `movie` : `tv`;
  111.         const response = await ToggleLikedFunc(mediaId, mediaType, defaultLanguage);
  112.         console.log(`there the problem?`);
  113.         if (response.status === 200) {
  114.             const themeKey: AppContextTypes["theme"] =
  115.                 theme === "system" ? (systemTheme ? "dark" : "light") : theme;
  116.             toast.success(`${response.message}`, {
  117.                 style: {
  118.                     background: `${themeKey ? "#2a2a2a5d" : "#ffffffe9"} `,
  119.                     borderRadius: "30px",
  120.                     color: `${themeKey === `dark` ? "#fff" : "#fff"}`,
  121.                 },
  122.                 iconTheme: {
  123.                     primary: `#452fde`,
  124.                     secondary: `#fff`,
  125.                 },
  126.             });
  127.             setChecked(!checked);
  128.         }
  129.     };
  130.     /**
  131.      * Formats the vote average by dividing it by 2 and returning a string representation with one decimal place.
  132.      *
  133.      * @param {number} vote_average - The vote average to be formatted
  134.      * @returns {string} - The formatted vote average
  135.      */
  136.     const handleVoteAverage = (vote_average: number): string =>
  137.         Number(vote_average).toFixed(1);
  138.  
  139.     return (
  140.         <>
  141.             <Modal
  142.                 backdrop="blur"
  143.                 className="bg-transparent/70 shadow-xl text-white "
  144.                 isOpen={isOpen}
  145.                 onOpenChange={onOpenChange}
  146.                 classNames={{
  147.                     backdrop: "  ",
  148.                 }}
  149.                 size="5xl"
  150.                 placement="center"
  151.                 shadow="lg"
  152.                 motionProps={{
  153.                     variants: {
  154.                         enter: {
  155.                             y: 0,
  156.                             opacity: 1,
  157.                             transition: {
  158.                                 duration: 0.3,
  159.                                 ease: "easeOut",
  160.                             },
  161.                         },
  162.                         exit: {
  163.                             y: -20,
  164.                             opacity: 0,
  165.                             transition: {
  166.                                 duration: 0.2,
  167.                                 ease: "easeIn",
  168.                             },
  169.                         },
  170.                     },
  171.                 }}
  172.             >
  173.                 <ModalContent>
  174.                     {(onClose) => (
  175.                         <>
  176.                             {isLoading || mediaDetails.length === 0 ? (
  177.                                 <Card className="w-[200px] space-y-5 p-4" radius="lg">
  178.                                     <Skeleton className="rounded-lg">
  179.                                         <div className="h-24 rounded-lg bg-default-300"></div>
  180.                                     </Skeleton>
  181.                                     <div className="space-y-3">
  182.                                         <Skeleton className="w-3/5 rounded-lg">
  183.                                             <div className="h-3 w-3/5 rounded-lg bg-default-200"></div>
  184.                                         </Skeleton>
  185.                                         <Skeleton className="w-4/5 rounded-lg">
  186.                                             <div className="h-3 w-4/5 rounded-lg bg-default-200"></div>
  187.                                         </Skeleton>
  188.                                         <Skeleton className="w-2/5 rounded-lg">
  189.                                             <div className="h-3 w-2/5 rounded-lg bg-default-300"></div>
  190.                                         </Skeleton>
  191.                                     </div>
  192.                                 </Card>
  193.                             ) : (
  194.                                 <>
  195.                                     {/* { console.log(`name of tv series`, mediaDetails) } */}
  196.                                     <ModalHeader className="flex flex-row gap-4 jusitfy-center items-center select-none">
  197.                                         {isMoviesMedia && mediaDetails
  198.                                             ? mediaDetails.title
  199.                                             : mediaDetails.name}
  200.                                         <Button
  201.                                             className="bg-transparent hover:bg-[#452fde]/30"
  202.                                             radius="full"
  203.                                             isIconOnly
  204.                                             onPress={() => {
  205.                                                 if (accessToken) {
  206.                                                     handleToggleLiked();
  207.                                                 } else {
  208.                                                     onOpen();
  209.                                                 }
  210.                                             }}
  211.                                         >
  212.                                             {checked ? (
  213.                                                 <Image src={circleCheck} width={20} />
  214.                                             ) : (
  215.                                                 <PlusCircle size={20} color={"white"} />
  216.                                             )}
  217.                                         </Button>
  218.                                     </ModalHeader>
  219.                                     <ModalBody>
  220.                                         <div className="flex flex-col sm:flex-row gap-[20px] sm:gap-[40px] justify-between items-center">
  221.                                             {/* first section */}
  222.                                             <div className="flex flex-col gap-1">
  223.                                                 {/* rating/release year/runtime */}
  224.                                                 <div className="flex h-5 items-center space-x-3 text-small">
  225.                                                     {/* rating */}
  226.                                                     <div className="flex flex-row space-x-2 items-center">
  227.                                                         <p className="text-white">
  228.                                                             <StarIcon size={18} />
  229.                                                         </p>
  230.                                                         <p className="text-base select-none">
  231.                                                             {handleVoteAverage(mediaDetails.vote_average)}
  232.                                                         </p>
  233.                                                     </div>
  234.                                                     <Divider orientation="vertical" />
  235.                                                     {/* release year */}
  236.                                                     <p className="text-base select-none">
  237.                                                         {isMoviesMedia
  238.                                                             ? handleReleaseDate(mediaDetails.release_date)
  239.                                                             : handleReleaseDate(mediaDetails.first_air_date)}
  240.                                                     </p>
  241.                                                     <Divider orientation="vertical" />
  242.                                                     {/* runtime */}
  243.                                                     <div className="flex flex-row space-x-1 items-center">
  244.                                                         <p className="text-base select-none">
  245.                                                             {runtime ? runtime : "NaN"}
  246.                                                         </p>
  247.                                                         <p className="text-base select-none">{min}</p>
  248.                                                     </div>
  249.                                                 </div>
  250.                                                 {/* country/genre/overview */}
  251.                                                 <div className="flex flex-col gap-1">
  252.                                                     {/* country */}
  253.                                                     <div className="flex flex-row space-x-1">
  254.                                                         <p className="text-base opacity-30 select-none">
  255.                                                             {country} :
  256.                                                         </p>
  257.                                                         <div className="flex flex-col space-x-1 sm:flex-row select-none">
  258.                                                             {mediaDetails ? (
  259.                                                                 <>
  260.                                                                     {isMoviesMedia !== null
  261.                                                                         ? mediaDetails.production_countries &&
  262.                                                                             mediaDetails.production_countries.map(
  263.                                                                                 (country, idx) => (
  264.                                                                                     <div
  265.                                                                                         key={idx}
  266.                                                                                         className="flex flex-row"
  267.                                                                                     >
  268.                                                                                         <p className="text-base opacity-50 hover:opacity-100 cursor-pointer">
  269.                                                                                             {country.name}
  270.                                                                                         </p>
  271.                                                                                         <p className="text-base opacity-50 select-none">
  272.                                                                                             {idx !==
  273.                                                                                             mediaDetails.production_countries
  274.                                                                                                 .length -
  275.                                                                                                 1
  276.                                                                                                 ? ", "
  277.                                                                                                 : ""}
  278.                                                                                         </p>
  279.                                                                                     </div>
  280.                                                                                 )
  281.                                                                             )
  282.                                                                         : mediaDetails.origin_country &&
  283.                                                                             mediaDetails.origin_country.map(
  284.                                                                                 (country, idx) => (
  285.                                                                                     <div
  286.                                                                                         key={idx}
  287.                                                                                         className="flex flex-row"
  288.                                                                                     >
  289.                                                                                         <p className="text-base opacity-50 hover:opacity-100 cursor-pointer">
  290.                                                                                             {country.name}
  291.                                                                                         </p>
  292.                                                                                         <p className="text-base opacity-50 select-none">
  293.                                                                                             {idx !==
  294.                                                                                             mediaDetails.origin_country
  295.                                                                                                 .length -
  296.                                                                                                 1
  297.                                                                                                 ? ", "
  298.                                                                                                 : ""}
  299.                                                                                         </p>
  300.                                                                                     </div>
  301.                                                                                 )
  302.                                                                             )}
  303.                                                                 </>
  304.                                                             ) : (
  305.                                                                 <></>
  306.                                                             )}
  307.                                                         </div>
  308.                                                     </div>
  309.                                                     {/* genre */}
  310.                                                     <div className="flex flex-row space-x-1 mb-4">
  311.                                                         <p className="text-base opacity-30 select-none">
  312.                                                             {genre} :
  313.                                                         </p>
  314.                                                         {mediaDetails ? (
  315.                                                             <>
  316.                                                                 {mediaDetails.genres &&
  317.                                                                     mediaDetails.genres.map((genre, idx) => (
  318.                                                                         <div
  319.                                                                             key={idx}
  320.                                                                             className="flex flex-row select-none"
  321.                                                                         >
  322.                                                                             <p className="text-base opacity-50 hover:opacity-100 cursor-pointer">
  323.                                                                                 {genre.name}
  324.                                                                             </p>
  325.                                                                             <p className="text-base opacity-50">
  326.                                                                                 {idx !== mediaDetails.genres.length - 1
  327.                                                                                     ? ", "
  328.                                                                                     : ""}
  329.                                                                             </p>
  330.                                                                         </div>
  331.                                                                     ))}
  332.                                                             </>
  333.                                                         ) : (
  334.                                                             <></>
  335.                                                         )}
  336.                                                     </div>
  337.                                                     {/* overview */}
  338.                                                     <div>
  339.                                                         {mediaDetails ? (
  340.                                                             <p className="text-base opacity-50 select-none">
  341.                                                                 {mediaDetails?.overview?.length > 150 &&
  342.                                                                     mediaDetails?.overview.substring(0, 150) +
  343.                                                                         "..."}
  344.                                                             </p>
  345.                                                         ) : (
  346.                                                             <></>
  347.                                                         )}
  348.                                                     </div>
  349.                                                 </div>
  350.                                             </div>
  351.                                             {/* second section */}
  352.                                             <div>
  353.                                                 <iframe
  354.                                                     className=" min-w-[45vh]  min-h-[200px] sm:w-full select-none"
  355.                                                     src={`https://www.youtube.com/embed/${trailer}`}
  356.                                                     allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
  357.                                                     title="YouTube video player"
  358.                                                     allowFullScreen
  359.                                                 ></iframe>
  360.                                             </div>
  361.                                         </div>
  362.                                     </ModalBody>
  363.                                     <ModalFooter>
  364.                                         <Button
  365.                                             radius="full"
  366.                                             color="danger"
  367.                                             variant="light"
  368.                                             onPress={onClose}
  369.                                         >
  370.                                             {closeBtn}
  371.                                         </Button>
  372.                                         <Button
  373.                                             radius="full"
  374.                                             color="primary"
  375.                                             variant="shadow"
  376.                                             onPress={() => {
  377.                                                 navigate(
  378.                                                     `${isMoviesMedia ? "/movie" : "/tv"}/${mediaDetails.id}${isMoviesMedia ? "" : "/1/1"}`
  379.                                                 ); onClose();}
  380.                                             }
  381.                                         >
  382.                                             {playBtn}
  383.                                             <Play size={16} />
  384.                                         </Button>
  385.                                     </ModalFooter>
  386.                                 </>
  387.                             )}
  388.                         </>
  389.                     )}
  390.                 </ModalContent>
  391.             </Modal>
  392.         </>
  393.     );
  394. };
  395.  
  396. export default MediaModal;
  397.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement