diff options
| author | Factiven <[email protected]> | 2023-07-16 22:35:39 +0700 |
|---|---|---|
| committer | Factiven <[email protected]> | 2023-07-16 22:35:39 +0700 |
| commit | 1eee181e219dfd993d396ac3169e7aad3dd285eb (patch) | |
| tree | 23fe54e9c3f8810f3ac9ab6b29070b4f0d4b9d20 /pages/id/index.js | |
| parent | removed console.log (diff) | |
| download | moopa-1eee181e219dfd993d396ac3169e7aad3dd285eb.tar.xz moopa-1eee181e219dfd993d396ac3169e7aad3dd285eb.zip | |
Update v3.6.4
- Added Manga page with a working tracker for AniList user
- Added schedule component to home page
- Added disqus comment section so you can fight on each other (not recommended)
- Added /id and /en route for english and indonesian subs (id route still work in progress)
Diffstat (limited to 'pages/id/index.js')
| -rw-r--r-- | pages/id/index.js | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/pages/id/index.js b/pages/id/index.js new file mode 100644 index 0000000..1d42ce3 --- /dev/null +++ b/pages/id/index.js @@ -0,0 +1,633 @@ +import { aniListData } from "../../lib/anilist/AniList"; +import React, { useState, useEffect } from "react"; +import Head from "next/head"; +import Link from "next/link"; +import Footer from "../../components/footer"; +import Image from "next/image"; +import Content from "../../components/home/content"; +import { useRouter } from "next/router"; + +import { motion } from "framer-motion"; + +import { useSession, signIn, signOut } from "next-auth/react"; +import { useAniList } from "../../lib/anilist/useAnilist"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../api/auth/[...nextauth]"; +import SearchBar from "../../components/searchBar"; +import Genres from "../../components/home/genres"; +import { ToastContainer, toast, cssTransition } from "react-toastify"; + +export function Navigasi() { + const { data: sessions, status } = useSession(); + const [year, setYear] = useState(new Date().getFullYear()); + const [season, setSeason] = useState(getCurrentSeason()); + + const router = useRouter(); + + const handleFormSubmission = (inputValue) => { + router.push(`/id/search/${encodeURIComponent(inputValue)}`); + }; + + const handleKeyDown = async (event) => { + if (event.key === "Enter") { + event.preventDefault(); + const inputValue = event.target.value; + handleFormSubmission(inputValue); + } + }; + return ( + <> + {/* NAVBAR PC */} + <div className="flex items-center justify-center"> + <div className="flex w-full items-center justify-between px-5 lg:mx-[94px]"> + <div className="flex items-center lg:gap-16 lg:pt-7"> + <Link + href="/id/" + className=" font-outfit lg:text-[40px] text-[30px] font-bold text-[#FF7F57]" + > + moopa + </Link> + <ul className="hidden items-center gap-10 pt-2 font-outfit text-[14px] lg:flex"> + <li> + <Link + href={`/id/search/anime?season=${season}&seasonYear=${year}`} + > + This Season + </Link> + </li> + <li> + <Link href="/id/search/manga">Manga</Link> + </li> + <li> + <Link href="/id/search/anime">Anime</Link> + </li> + + {status === "loading" ? ( + <li>Loading...</li> + ) : ( + <> + {!sessions && ( + <li> + <button + onClick={() => signIn("AniListProvider")} + className="ring-1 ring-action font-karla font-bold px-2 py-1 rounded-md" + > + Sign in + </button> + </li> + )} + {sessions && ( + <li className="text-center"> + <Link href={`/id/profile/${sessions?.user.name}`}> + My List + </Link> + </li> + )} + </> + )} + </ul> + </div> + <div className="relative flex lg:scale-75 scale-[65%] items-center mb-7 lg:mb-0"> + <div className="search-box "> + <input + className="search-text" + type="text" + placeholder="Search Anime" + onKeyDown={handleKeyDown} + /> + <div className="search-btn"> + <i className="fas fa-search"></i> + </div> + </div> + </div> + </div> + </div> + </> + ); +} + +export default function Home({ detail, populars, sessions }) { + const { media: current } = useAniList(sessions, { stats: "CURRENT" }); + const { media: plan } = useAniList(sessions, { stats: "PLANNING" }); + + const [isVisible, setIsVisible] = useState(false); + const [list, setList] = useState(null); + const [planned, setPlanned] = useState(null); + const [greeting, setGreeting] = useState(""); + const [onGoing, setOnGoing] = useState(null); + + const [prog, setProg] = useState(null); + + const popular = populars?.data; + const data = detail.data[0]; + + const handleShowClick = () => { + setIsVisible(true); + }; + + const handleHideClick = () => { + setIsVisible(false); + }; + + useEffect(() => { + const time = new Date().getHours(); + let greeting = ""; + + if (time >= 5 && time < 12) { + greeting = "Good morning"; + } else if (time >= 12 && time < 18) { + greeting = "Good afternoon"; + } else if (time >= 18 && time < 22) { + greeting = "Good evening"; + } else if (time >= 22 || time < 5) { + greeting = "Good night"; + } + + setGreeting(greeting); + + async function userData() { + if (!sessions) return; + const getMedia = + current.filter((item) => item.status === "CURRENT")[0] || null; + const list = getMedia?.entries + .map(({ media }) => media) + .filter((media) => media); + + const prog = getMedia?.entries.filter( + (item) => item.media.nextAiringEpisode !== null + ); + + setProg(prog); + + const planned = plan?.[0]?.entries + .map(({ media }) => media) + .filter((media) => media); + + const onGoing = list?.filter((item) => item.nextAiringEpisode !== null); + setOnGoing(onGoing); + + if (list) { + setList(list.reverse()); + } + if (planned) { + setPlanned(planned.reverse()); + } + } + userData(); + }, [sessions, current, plan]); + + const blurSlide = cssTransition({ + enter: "slide-in-blurred-right", + exit: "slide-out-blurred-right", + }); + + useEffect(() => { + function Toast() { + toast.warn( + "This site is still in development, some features may not work properly.", + { + position: "bottom-right", + autoClose: false, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + theme: "dark", + transition: blurSlide, + } + ); + } + Toast(); + }, []); + + // console.log(log); + + return ( + <> + <Head> + <title>Moopa</title> + <meta charSet="UTF-8"></meta> + <meta name="twitter:card" content="summary_large_image" /> + <meta + name="twitter:title" + content="Moopa - Free Anime and Manga Streaming" + /> + <meta + name="twitter:description" + content="Discover your new favorite anime or manga title! Moopa offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Moopa today!" + /> + <meta + name="twitter:image" + content="https://cdn.discordapp.com/attachments/1084446049986420786/1093300833422168094/image.png" + /> + <link rel="icon" href="/c.svg" /> + </Head> + + <ToastContainer pauseOnFocusLoss={false} style={{ width: "420px" }} /> + + {/* NAVBAR */} + <div className="z-50"> + {!isVisible && ( + <button + onClick={handleShowClick} + className="fixed bottom-[30px] right-[20px] z-[100] flex h-[51px] w-[50px] cursor-pointer items-center justify-center rounded-[8px] bg-[#17171f] shadow-lg lg:hidden" + id="bars" + > + <svg + xmlns="http://www.w3.org/2000/svg" + className="h-[42px] w-[61.5px] text-[#8BA0B2] fill-orange-500" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" + clipRule="evenodd" + /> + </svg> + </button> + )} + </div> + + {/* Mobile Menu */} + <div className={`transition-all duration-150 subpixel-antialiased z-50`}> + {isVisible && sessions && ( + <Link + href={`/profile/${sessions?.user.name}`} + className="fixed lg:hidden bottom-[100px] w-[60px] h-[60px] flex items-center justify-center right-[20px] rounded-full z-50 bg-[#17171f]" + > + <img + src={sessions?.user.image.large} + alt="user avatar" + className="object-cover w-[60px] h-[60px] rounded-full" + /> + </Link> + )} + {isVisible && ( + <div className="fixed bottom-[30px] right-[20px] z-50 flex h-[51px] w-[300px] items-center justify-center gap-8 rounded-[8px] text-[11px] bg-[#17171f] shadow-lg lg:hidden"> + <div className="grid grid-cols-4 place-items-center gap-6"> + <button className="group flex flex-col items-center"> + <Link href="/id/" className=""> + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + strokeWidth={1.5} + stroke="currentColor" + className="w-6 h-6 group-hover:stroke-action" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" + /> + </svg> + </Link> + <Link + href="/id/" + className="font-karla font-bold text-[#8BA0B2] group-hover:text-action" + > + home + </Link> + </button> + <button className="group flex flex-col items-center"> + <Link href="/id/about"> + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + strokeWidth={1.5} + stroke="currentColor" + className="w-6 h-6 group-hover:stroke-action" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" + /> + </svg> + </Link> + <Link + href="/id/about" + className="font-karla font-bold text-[#8BA0B2] group-hover:text-action" + > + about + </Link> + </button> + <button className="group flex gap-[1.5px] flex-col items-center "> + <div> + <Link href="/id/search/anime"> + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + strokeWidth={1.5} + stroke="currentColor" + className="w-6 h-6 group-hover:stroke-action" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" + /> + </svg> + </Link> + </div> + <Link + href="/id/search/anime" + className="font-karla font-bold text-[#8BA0B2] group-hover:text-action" + > + search + </Link> + </button> + {sessions ? ( + <button + onClick={() => signOut("AniListProvider")} + className="group flex gap-[1.5px] flex-col items-center " + > + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 96 960 960" + className="group-hover:fill-action w-6 h-6 fill-txt" + > + <path d="M186.666 936q-27 0-46.833-19.833T120 869.334V282.666q0-27 19.833-46.833T186.666 216H474v66.666H186.666v586.668H474V936H186.666zm470.668-176.667l-47-48 102-102H370v-66.666h341.001l-102-102 46.999-48 184 184-182.666 182.666z"></path> + </svg> + </div> + <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"> + logout + </h1> + </button> + ) : ( + <button + onClick={() => signIn("AniListProvider")} + className="group flex gap-[1.5px] flex-col items-center " + > + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 96 960 960" + className="group-hover:fill-action w-6 h-6 fill-txt mr-2" + > + <path d="M486 936v-66.666h287.334V282.666H486V216h287.334q27 0 46.833 19.833T840 282.666v586.668q0 27-19.833 46.833T773.334 936H486zm-78.666-176.667l-47-48 102-102H120v-66.666h341l-102-102 47-48 184 184-182.666 182.666z"></path> + </svg> + </div> + <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"> + login + </h1> + </button> + )} + </div> + <button onClick={handleHideClick}> + <svg + width="20" + height="21" + className="fill-orange-500" + viewBox="0 0 20 21" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <rect + x="2.44043" + y="0.941467" + width="23.5842" + height="3.45134" + rx="1.72567" + transform="rotate(45 2.44043 0.941467)" + /> + <rect + x="19.1172" + y="3.38196" + width="23.5842" + height="3.45134" + rx="1.72567" + transform="rotate(135 19.1172 3.38196)" + /> + </svg> + </button> + </div> + )} + </div> + + <div className="h-auto w-screen bg-[#141519] text-[#dbdcdd] "> + <Navigasi /> + <SearchBar /> + {/* PC / TABLET */} + <div className=" hidden justify-center lg:flex my-16"> + <div className="relative grid grid-rows-2 items-center lg:flex lg:h-[467px] lg:w-[80%] lg:justify-between"> + <div className="row-start-2 flex h-full flex-col gap-7 lg:w-[55%] lg:justify-center"> + <h1 className="w-[85%] font-outfit font-extrabold lg:text-[34px] line-clamp-2"> + {data.title.english || data.title.romaji || data.title.native} + </h1> + <p + className="font-roboto font-light lg:text-[18px] line-clamp-5" + dangerouslySetInnerHTML={{ __html: data?.description }} + /> + + <div className="lg:pt-5"> + <Link + href={`/id/anime/${data.id}`} + legacyBehavior + className="flex" + > + <a className="rounded-sm p-3 text-md font-karla font-light ring-1 ring-[#FF7F57]"> + START WATCHING + </a> + </Link> + </div> + </div> + <div className="z-10 row-start-1 flex justify-center "> + <div className="relative lg:h-[467px] lg:w-[322px] lg:scale-100"> + <div className="absolute bg-gradient-to-t from-[#141519] to-transparent lg:h-[467px] lg:w-[322px]" /> + + <Image + draggable={false} + src={data.coverImage?.extraLarge || data.image} + alt={`alt for ${data.title.english || data.title.romaji}`} + width={460} + height={662} + priority + className="rounded-tl-xl rounded-tr-xl object-cover bg-blend-overlay lg:h-[467px] lg:w-[322px]" + /> + </div> + </div> + </div> + </div> + {/* {!sessions && ( + <h1 className="font-bold font-karla mx-5 text-[32px] mt-2 lg:mx-24 xl:mx-36"> + {greeting}! + </h1> + )} */} + {sessions && ( + <div className="flex items-center justify-center lg:bg-none mt-4 lg:mt-0 w-screen"> + <div className="lg:w-[85%] w-screen px-5 lg:px-0 lg:text-4xl flex items-center gap-3 text-2xl font-bold font-karla"> + {greeting},<h1 className="lg:hidden">{sessions?.user.name}</h1> + <button + onClick={() => signOut()} + className="hidden text-center relative lg:flex justify-center group" + > + {sessions?.user.name} + <span className="absolute text-sm z-50 w-20 text-center bottom-11 text-white shadow-lg opacity-0 bg-secondary p-1 rounded-md font-karla font-light invisible group-hover:visible group-hover:opacity-100 duration-300 transition-all"> + Sign Out + </span> + </button> + </div> + </div> + )} + + <div className="lg:mt-16 mt-5 flex flex-col items-center"> + <motion.div + className="w-screen flex-none lg:w-[87%]" + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + transition={{ duration: 0.5, staggerChildren: 0.2 }} // Add staggerChildren prop + > + {sessions && onGoing?.length > 0 && ( + <motion.div // Add motion.div to each child component + key="onGoing" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="onGoing" + section="On-Going Anime" + data={onGoing} + og={prog} + /> + </motion.div> + )} + + {sessions && list?.length > 0 && ( + <motion.div // Add motion.div to each child component + key="listAnime" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="listAnime" + section="Your Watch List" + data={list} + /> + </motion.div> + )} + + {/* SECTION 2 */} + {sessions && planned?.length > 0 && ( + <motion.div // Add motion.div to each child component + key="plannedAnime" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="plannedAnime" + section="Your Plan" + data={planned} + /> + </motion.div> + )} + + {/* SECTION 3 */} + {detail && ( + <motion.div // Add motion.div to each child component + key="trendingAnime" + initial={{ y: 20, opacity: 0 }} + transition={{ duration: 0.5 }} + whileInView={{ y: 0, opacity: 1 }} + viewport={{ once: true }} + > + <Content + ids="trendingAnime" + section="Trending Now" + data={detail.data} + /> + </motion.div> + )} + + {/* SECTION 4 */} + {popular && ( + <motion.div // Add motion.div to each child component + key="popularAnime" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="popularAnime" + section="Popular Anime" + data={popular} + /> + </motion.div> + )} + + <motion.div // Add motion.div to each child component + key="Genres" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Genres /> + </motion.div> + </motion.div> + </div> + </div> + <Footer /> + </> + ); +} + +export async function getServerSideProps(context) { + const session = await getServerSession(context.req, context.res, authOptions); + + const trendingDetail = await aniListData({ + sort: "TRENDING_DESC", + page: 1, + }); + const popularDetail = await aniListData({ + sort: "POPULARITY_DESC", + page: 1, + }); + const genreDetail = await aniListData({ sort: "TYPE", page: 1 }); + + return { + props: { + genre: genreDetail.props, + detail: trendingDetail.props, + populars: popularDetail.props, + sessions: session, + }, + }; +} + +function getCurrentSeason() { + const now = new Date(); + const month = now.getMonth() + 1; // getMonth() returns 0-based index + + switch (month) { + case 12: + case 1: + case 2: + return "WINTER"; + case 3: + case 4: + case 5: + return "SPRING"; + case 6: + case 7: + case 8: + return "SUMMER"; + case 9: + case 10: + case 11: + return "FALL"; + default: + return "UNKNOWN SEASON"; + } +} |