NishilSheth

Untitled

Sep 13th, 2023
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import React, {
  2.     useState,
  3.     useRef,
  4.     useEffect,
  5.     useContext,
  6.     Fragment,
  7. } from "react";
  8.  
  9. import { Poppins } from "next/font/google";
  10. import { Rubik } from "next/font/google";
  11. import {
  12.     BsFillPlayFill,
  13.     BsPauseFill,
  14.     BsFullscreen,
  15.     BsFullscreenExit,
  16.     BsVolumeUpFill,
  17.     BsVolumeMuteFill,
  18.     BsVolumeDownFill,
  19.     BsPlayCircle,
  20.     BsArrowLeft,
  21.     BsCheck2,
  22.     BsLink45Deg,
  23. } from "react-icons/bs";
  24. import { MdSettings, MdKeyboardArrowDown } from "react-icons/md";
  25. import ReactPlayer from "react-player";
  26. import { Popover, Transition, Tab } from "@headlessui/react";
  27. import { PlayerState, playerContext } from "./context/playerContext";
  28. import debounce from "lodash.debounce";
  29. import Chat from "./components/Chat/chat";
  30.  
  31. import io from "socket.io-client";
  32.  
  33. const poppins = Poppins({
  34.     weight: ["500", "600", "700", "800"],
  35.     subsets: ["latin"],
  36.     display: "swap",
  37. });
  38.  
  39. const rubik = Rubik({
  40.     weight: ["300", "400", "500", "800", "900"],
  41.     subsets: ["latin"],
  42.     display: "swap",
  43. });
  44.  
  45. // TODO: Implement Cursor hide when showControls is false
  46.  
  47. const Room = () => {
  48.     const {
  49.         hasWindow,
  50.         setHasWindow,
  51.         isPlayerReady,
  52.         setIsPlayerReady,
  53.         play,
  54.         setPlay,
  55.         isFullScreen,
  56.         setIsFullScreen,
  57.         volume,
  58.         setVolume,
  59.         url,
  60.         setUrl,
  61.         isDropdownOpen,
  62.         setIsDropdownOpen,
  63.         speed,
  64.         setSpeed,
  65.         playtime,
  66.         setPlaytime,
  67.         setActiveMenu,
  68.         fileUrl,
  69.         setFileUrl,
  70.     } = useContext(playerContext);
  71.  
  72.     const [showControls, setShowControls] = useState(true);
  73.     const [isMouseVisible, setIsMouseVisible] = useState(true);
  74.     const [isArrowSeeking, setIsArrowSeeking] = useState(false);
  75.     const timerRef = useRef(null);
  76.  
  77.     const playerRef = useRef();
  78.     const rangeInputRef = useRef();
  79.     const vidSectionRef = useRef();
  80.     const fullScreenBtnRef = useRef();
  81.     const volumeInputRef = useRef();
  82.     const urlInputRef = useRef();
  83.     const controlsDeactivationTime = 3000;
  84.  
  85.     // const socket = io("http://localhost:5000");
  86.  
  87.     const handlePlayerReady = () => {
  88.         if (playerRef.current) {
  89.             setIsPlayerReady(true);
  90.         }
  91.     };
  92.  
  93.     const handleRangeChange = (e) => {
  94.         playerRef.current.seekTo(e.target.value, "seconds");
  95.         setPlaytime(e.target.value);
  96.     };
  97.  
  98.     const handleProgressChange = () => {
  99.         // get the current time of video
  100.         // update the value of range input to current time of video
  101.         if (rangeInputRef.current) {
  102.             rangeInputRef.current.value = Math.round(
  103.                 playerRef.current.getCurrentTime()
  104.             );
  105.             if (
  106.                 formatTime(playerRef.current.getDuration()) ===
  107.                 formatTime(Math.round(playerRef.current.getCurrentTime()))
  108.             ) {
  109.                 setPlay(false);
  110.             }
  111.         }
  112.         setPlaytime(Math.round(playerRef.current.getCurrentTime()));
  113.     };
  114.  
  115.     const handleFullScreen = (e) => {
  116.         if (isFullScreen) {
  117.             if (document.exitFullscreen) {
  118.                 document.exitFullscreen();
  119.             }
  120.         } else {
  121.             if (vidSectionRef.current.requestFullscreen) {
  122.                 vidSectionRef.current.requestFullscreen();
  123.                 document.activeElement.blur(); // remove focus from the fullscreen button
  124.             }
  125.         }
  126.     };
  127.  
  128.     const toggleFullscreen = () => {
  129.         if (vidSectionRef.current) {
  130.             if (!(document.fullscreenElement === vidSectionRef.current)) {
  131.                 vidSectionRef.current.requestFullscreen();
  132.             } else {
  133.                 if (document.exitFullscreen) {
  134.                     document.exitFullscreen();
  135.                 }
  136.             }
  137.             setIsFullScreen(!isFullScreen);
  138.         }
  139.     };
  140.     const togglePlayState = () => {
  141.         setPlay((prevPlay) => !prevPlay);
  142.         document.activeElement.blur();
  143.     };
  144.  
  145.     const handleVolumeChange = (e) => {
  146.         setVolume(parseFloat(e.target.value));
  147.     };
  148.  
  149.     const formatTime = (seconds) => {
  150.         const date = new Date(null);
  151.         date.setSeconds(seconds);
  152.         if (date instanceof Date && !isNaN(date)) {
  153.             // Checking for a valid date object.
  154.             if (seconds > 3600) {
  155.                 return date.toISOString().slice(-13, -5);
  156.             } else {
  157.                 return date.toISOString().slice(-10, -5);
  158.             }
  159.         }
  160.     };
  161.  
  162.     useEffect(() => {
  163.         if (typeof window !== "undefined") {
  164.             setHasWindow(true);
  165.         }
  166.  
  167.         const handleFullScreenChange = () => {
  168.             setIsFullScreen(
  169.                 document.fullscreenElement === vidSectionRef.current
  170.             );
  171.         };
  172.  
  173.         // using debounce to prevent this from firing a lot of times if the arrow keys are pressed rapidly.
  174.         const handleKeyPress = debounce((event) => {
  175.             const activeElement = document.activeElement;
  176.             const excludedTags = ["INPUT", "TEXTAREA", "SELECT"];
  177.             if (
  178.                 excludedTags.includes(activeElement.tagName) &&
  179.                 !(activeElement.type === "range")
  180.             ) {
  181.                 return;
  182.             }
  183.  
  184.             switch (event.key) {
  185.                 case "f":
  186.                     toggleFullscreen();
  187.                     break;
  188.                 case " ":
  189.                     event.preventDefault();
  190.                     togglePlayState();
  191.                     handleMouseMove();
  192.                     break;
  193.  
  194.                 default:
  195.                     break;
  196.             }
  197.         }, 250);
  198.  
  199.         const handleMouseMove = () => {
  200.             clearTimeout(timerRef.current);
  201.             setShowControls(true);
  202.             setIsMouseVisible(true);
  203.  
  204.             console.log(vidSectionRef.current);
  205.  
  206.             timerRef.current = setTimeout(() => {
  207.                 setShowControls(false);
  208.                 setIsDropdownOpen(false);
  209.                 setIsMouseVisible(false);
  210.             }, controlsDeactivationTime); // Adjust the timeout value according to your preference (in milliseconds)
  211.         };
  212.  
  213.         if (vidSectionRef.current) {
  214.             vidSectionRef.current.addEventListener(
  215.                 "mousemove",
  216.                 handleMouseMove
  217.             );
  218.         }
  219.         document.addEventListener("keydown", handleKeyPress);
  220.  
  221.         document.addEventListener("fullscreenchange", handleFullScreenChange);
  222.  
  223.         // Cleanup
  224.         return () => {
  225.             document.removeEventListener(
  226.                 "fullscreenchange",
  227.                 handleFullScreenChange
  228.             );
  229.             document.removeEventListener("keydown", handleKeyPress);
  230.             handleKeyPress.cancel();
  231.             if (vidSectionRef.current) {
  232.                 vidSectionRef.current.removeEventListener(
  233.                     "mousemove",
  234.                     handleMouseMove
  235.                 );
  236.             }
  237.             clearTimeout(timerRef.current);
  238.         };
  239.     }, []);
  240.  
  241.     if (!hasWindow) {
  242.         return null;
  243.     }
  244.  
  245.     return (
  246.         <main className="mb-5 mt-7 flex flex-col gap-6 px-5 md:flex-row">
  247.             {/* Section 1: Video */}
  248.             <div
  249.                 className={`flex w-full flex-col gap-2 md:w-2/3 ${
  250.                     isFullScreen ? (isMouseVisible ? "" : "cursor-none") : ""
  251.                 }`}
  252.             >
  253.                 <div>
  254.                     <Tab.Group>
  255.                         <Tab.List className="flex h-12 items-center gap-4 rounded-md bg-gray-200 p-2">
  256.                             <Tab as={Fragment}>
  257.                                 {({ selected }) => (
  258.                                     <button
  259.                                         className={`${
  260.                                             selected ? "bg-black/20" : ""
  261.                                         } rounded-md px-2 py-1 outline-none transition-colors hover:bg-black/20`}
  262.                                     >
  263.                                         Video URL
  264.                                     </button>
  265.                                 )}
  266.                             </Tab>
  267.                             <Tab as={Fragment}>
  268.                                 {({ selected }) => (
  269.                                     <button
  270.                                         className={`${
  271.                                             selected ? "bg-black/20" : ""
  272.                                         } rounded-md px-2 py-1 outline-none transition-colors hover:bg-black/20`}
  273.                                     >
  274.                                         Custom Video
  275.                                     </button>
  276.                                 )}
  277.                             </Tab>
  278.                         </Tab.List>
  279.                         <Tab.Panels>
  280.                             <Tab.Panel className="mt-2 flex gap-2">
  281.                                 <input
  282.                                     ref={urlInputRef}
  283.                                     type="text"
  284.                                     className="w-full rounded-md border border-gray-400 bg-transparent px-3 py-2 font-light text-white outline-none placeholder:font-light"
  285.                                     placeholder="Enter Video URL here."
  286.                                 />
  287.                                 <button
  288.                                     onClick={(e) => {
  289.                                         setUrl(urlInputRef.current.value);
  290.                                         setFileUrl("");
  291.                                         console.log(e.target.value);
  292.                                     }}
  293.                                     className="rounded-md bg-red-600 px-2 py-1 text-white"
  294.                                 >
  295.                                     Submit
  296.                                 </button>
  297.                             </Tab.Panel>
  298.                             <Tab.Panel className="mt-2">
  299.                                 <label
  300.                                     htmlFor="file-choose"
  301.                                     className="cursor-pointer rounded-md bg-red-600 px-3 py-2 text-left text-white"
  302.                                 >
  303.                                     Choose File
  304.                                     <input
  305.                                         accept="video/*"
  306.                                         onChange={(e) => {
  307.                                             const file = e.target.files[0];
  308.                                             if (file) {
  309.                                                 const videoUrl =
  310.                                                     URL.createObjectURL(file);
  311.                                                 setFileUrl(videoUrl);
  312.                                                 setUrl("");
  313.                                             }
  314.                                         }}
  315.                                         id="file-choose"
  316.                                         type="file"
  317.                                         className="w-0 opacity-0"
  318.                                     />
  319.                                 </label>
  320.                             </Tab.Panel>
  321.                         </Tab.Panels>
  322.                     </Tab.Group>
  323.                 </div>
  324.                 <section
  325.                     ref={vidSectionRef}
  326.                     className="pointer-events-auto relative flex w-full select-none flex-col gap-3"
  327.                 >
  328.                     {hasWindow && (
  329.                         <>
  330.                             {url && (
  331.                                 <ReactPlayer
  332.                                     ref={playerRef}
  333.                                     url={url}
  334.                                     onReady={handlePlayerReady}
  335.                                     config={{
  336.                                         youtube: {
  337.                                             playerVars: { disablekb: 1 },
  338.                                         },
  339.                                     }}
  340.                                     className="pointer-events-none aspect-video"
  341.                                     width="100%"
  342.                                     height="100%"
  343.                                     playing={play}
  344.                                     onProgress={handleProgressChange}
  345.                                     onPlay={() => setPlay(true)}
  346.                                     onPause={() => setPlay(false)}
  347.                                     volume={volume}
  348.                                     playbackRate={speed}
  349.                                 />
  350.                             )}
  351.                             {fileUrl && (
  352.                                 <ReactPlayer
  353.                                     ref={playerRef}
  354.                                     url={fileUrl}
  355.                                     onReady={handlePlayerReady}
  356.                                     className="pointer-events-none aspect-video"
  357.                                     width="100%"
  358.                                     height="100%"
  359.                                     playing={play}
  360.                                     onProgress={handleProgressChange}
  361.                                     onPlay={() => setPlay(true)}
  362.                                     onPause={() => setPlay(false)}
  363.                                     volume={volume}
  364.                                     playbackRate={speed}
  365.                                     config={{
  366.                                         file: {
  367.                                             attributes: { controls: false },
  368.                                         },
  369.                                     }}
  370.                                 />
  371.                             )}
  372.                         </>
  373.                     )}
  374.                     {/* Controls */}
  375.                     {isPlayerReady &&
  376.                         ReactPlayer.canPlay(url) |
  377.                             ReactPlayer.canPlay(fileUrl) &&
  378.                         playerRef.current && (
  379.                             <div
  380.                                 className={`${
  381.                                     showControls ? "opacity-100" : "opacity-0"
  382.                                 } absolute bottom-0 flex w-full flex-col items-center gap-3 bg-gradient-to-t from-black via-black/70 to-transparent px-3 py-1 transition-opacity duration-300 md:px-5 md:py-2`}
  383.                             >
  384.                                 <input
  385.                                     className="range-input w-full accent-red-600"
  386.                                     type="range"
  387.                                     min={0}
  388.                                     max={playerRef.current.getDuration()}
  389.                                     onChange={handleRangeChange}
  390.                                     ref={rangeInputRef}
  391.                                 />
  392.                                 <div className="flex w-full items-center gap-3">
  393.                                     <button onClick={() => setPlay(!play)}>
  394.                                         {play ? (
  395.                                             <BsPauseFill className="text-3xl text-white" />
  396.                                         ) : (
  397.                                             <BsFillPlayFill className="text-3xl text-white" />
  398.                                         )}
  399.                                     </button>
  400.                                     <button className="hidden md:block">
  401.                                         {volume > 0.5 ? (
  402.                                             <BsVolumeUpFill className="text-3xl text-white" />
  403.                                         ) : volume > 0 ? (
  404.                                             <BsVolumeDownFill className="text-3xl text-white" />
  405.                                         ) : (
  406.                                             <BsVolumeMuteFill className="text-3xl text-white" />
  407.                                         )}
  408.                                     </button>
  409.                                     <input
  410.                                         className="volume-input hidden w-1/5 accent-red-600 md:block"
  411.                                         type="range"
  412.                                         min={0}
  413.                                         max={1}
  414.                                         step={"0.01"}
  415.                                         ref={volumeInputRef}
  416.                                         onChange={handleVolumeChange}
  417.                                     />
  418.                                     <span
  419.                                         className={`${poppins.className} mr-auto text-sm text-white`}
  420.                                     >
  421.                                         {formatTime(playtime)} /{" "}
  422.                                         {formatTime(
  423.                                             playerRef.current.getDuration()
  424.                                         )}
  425.                                     </span>
  426.                                     <div className="absolute bottom-10 right-5">
  427.                                         <DropdownMenu />
  428.                                     </div>
  429.                                     <button
  430.                                         onClick={() => {
  431.                                             setIsDropdownOpen(
  432.                                                 (prevState) => !prevState
  433.                                             );
  434.                                             setActiveMenu("main");
  435.                                         }}
  436.                                     >
  437.                                         <MdSettings className="text-2xl text-white" />
  438.                                     </button>
  439.                                     <button
  440.                                         ref={fullScreenBtnRef}
  441.                                         onClick={handleFullScreen}
  442.                                         // className="ml-auto"
  443.                                     >
  444.                                         {isFullScreen ? (
  445.                                             <BsFullscreenExit className="text-xl text-white" />
  446.                                         ) : (
  447.                                             <BsFullscreen className=" text-xl text-white" />
  448.                                         )}
  449.                                     </button>
  450.                                 </div>
  451.                             </div>
  452.                         )}
  453.                 </section>
  454.             </div>
  455.             {/* {Section 3: Chat Section} */}
  456.             {/* <Chat MembersDropdown={MembersDropdown} /> */}
  457.         </main>
  458.     );
  459. };
  460.  
  461. const DropdownMenu = () => {
  462.     const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
  463.     const { speed, setSpeed, isDropdownOpen, activeMenu, setActiveMenu, url } =
  464.         useContext(playerContext);
  465.  
  466.     const [icon, setIcon] = useState(<BsLink45Deg />);
  467.     const DropdownItem = (props) => {
  468.         return (
  469.             <button
  470.                 onClick={() => {
  471.                     props.goToMenu && setActiveMenu(props.goToMenu);
  472.                     props.onClickEvent && props.onClickEvent();
  473.                 }}
  474.                 className="flex w-44 items-center gap-2 p-3 text-white hover:bg-gray-700"
  475.             >
  476.                 <span>{props.leftIcon}</span>
  477.                 <span>{props.name}</span>
  478.             </button>
  479.         );
  480.     };
  481.  
  482.     return (
  483.         <>
  484.             <Transition
  485.                 show={isDropdownOpen}
  486.                 enter="transition-opacity duration-300"
  487.                 enterFrom="opacity-0"
  488.                 enterTo="opacity-1"
  489.                 leave="transition-opacity durarion-300"
  490.                 leaveFrom="opacity-100"
  491.                 leaveTo="opacity-0"
  492.             >
  493.                 <div className="max-h-52 overflow-y-auto bg-black/90">
  494.                     <Transition
  495.                         show={activeMenu !== "main"}
  496.                         enter="transition-all duration-300"
  497.                         enterFrom="opacity-0"
  498.                         enterTo="opacity-100"
  499.                         leave="transition-all duration-300"
  500.                         leaveFrom="opacity-100"
  501.                         leaveTo="opacity-0"
  502.                     >
  503.                         {activeMenu !== "main" && (
  504.                             <DropdownItem
  505.                                 goToMenu="main"
  506.                                 leftIcon={<BsArrowLeft />}
  507.                                 name="Playback Speed"
  508.                             />
  509.                         )}
  510.                     </Transition>
  511.                     <Transition
  512.                         show={activeMenu === "main"}
  513.                         enter="transition-all duration-300"
  514.                         enterFrom="opacity-0"
  515.                         enterTo="opacity-100"
  516.                         leave="transition-all duration-300"
  517.                         leaveFrom="opacity-100"
  518.                         leaveTo="opacity-0"
  519.                     >
  520.                         <DropdownItem
  521.                             goToMenu="Playback Speed"
  522.                             leftIcon={<BsPlayCircle />}
  523.                             name="PlayBack Speed"
  524.                         />
  525.                         {url && (
  526.                             <DropdownItem
  527.                                 // goToMenu="Playback Speed"
  528.                                 onClickEvent={() => {
  529.                                     navigator.clipboard.writeText(url);
  530.                                     setIcon(<BsCheck2 />);
  531.                                     setTimeout(() => {
  532.                                         setIcon(
  533.                                             <BsLink45Deg className="text-lg" />
  534.                                         );
  535.                                     }, 1500);
  536.                                 }}
  537.                                 leftIcon={icon}
  538.                                 name="Copy Video URL"
  539.                             />
  540.                         )}
  541.                     </Transition>
  542.                     <Transition
  543.                         show={activeMenu === "Playback Speed"}
  544.                         enter="transition-all duration-500"
  545.                         enterFrom="opacity-0 -translate-x-3 h-0"
  546.                         enterTo="opacity-100 translate-x-0 h-auto"
  547.                         leave="transition-all duration-500"
  548.                         leaveFrom="opacity-100 translate-x-0 h-auto"
  549.                         leaveTo="opacity-0 -translate-x-3 h-0"
  550.                     >
  551.                         <div className="flex flex-col gap-2">
  552.                             {speeds.map((s, index) => {
  553.                                 return (
  554.                                     <DropdownItem
  555.                                         onClickEvent={() => setSpeed(s)}
  556.                                         name={s}
  557.                                         key={index}
  558.                                         leftIcon={
  559.                                             s === speed ? <BsCheck2 /> : ""
  560.                                         }
  561.                                     />
  562.                                 );
  563.                             })}
  564.                         </div>
  565.                     </Transition>
  566.                 </div>
  567.             </Transition>
  568.         </>
  569.     );
  570. };
  571.  
  572. const MembersDropdown = (props) => {
  573.     const { members, setMembers } = useContext(playerContext);
  574.     return (
  575.         <div>
  576.             <Popover>
  577.                 <Popover.Button className="flex w-40 items-center justify-between gap-2 rounded-md border-none bg-neutral-700 px-2 py-1 text-white outline-none focus:ring-2 focus:ring-white">
  578.                     <span className="text-base">Members</span>
  579.                     <MdKeyboardArrowDown />
  580.                 </Popover.Button>
  581.                 <Transition
  582.                     enter="transition-opacity transition-transform duration-200 ease-out"
  583.                     enterFrom="opacity-0 translate-y-1"
  584.                     enterTo="opacity-100 translate-y-0"
  585.                     leave="transition-opacity transition-transform duration-200 ease-in"
  586.                     leaveFrom="opacity-100 translate-y-0"
  587.                     leaveTo="opacity-0 translate-y-1"
  588.                 >
  589.                     <Popover.Panel>
  590.                         <ol className="divide-y-2 divide-black/30 rounded-md bg-neutral-300">
  591.                             {members.map((member, index) => {
  592.                                 return (
  593.                                     <li
  594.                                         key={index}
  595.                                         className={` ${poppins.className} list-inside list-decimal p-2 text-left text-base text-black`}
  596.                                     >
  597.                                         {member}
  598.                                     </li>
  599.                                 );
  600.                             })}
  601.                         </ol>
  602.                     </Popover.Panel>
  603.                 </Transition>
  604.             </Popover>
  605.         </div>
  606.     );
  607. };
  608.  
  609. export default Room;
  610.  
Advertisement
Add Comment
Please, Sign In to add comment