diff options
| author | Factiven <[email protected]> | 2023-09-13 00:45:53 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-13 00:45:53 +0700 |
| commit | 7327a69b55a20b99b14ee0803d6cf5f8b88c45ef (patch) | |
| tree | cbcca777593a8cc4b0282e7d85a6fc51ba517e25 /pages/en/anime/recent.js | |
| parent | Update issue templates (diff) | |
| download | moopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.tar.xz moopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.zip | |
Update v4 - Merge pre-push to main (#71)
* Create build-test.yml
* initial v4 commit
* update: github workflow
* update: push on branch
* Update .github/ISSUE_TEMPLATE/bug_report.md
* configuring next.config.js file
Diffstat (limited to 'pages/en/anime/recent.js')
| -rw-r--r-- | pages/en/anime/recent.js | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/pages/en/anime/recent.js b/pages/en/anime/recent.js new file mode 100644 index 0000000..89a868a --- /dev/null +++ b/pages/en/anime/recent.js @@ -0,0 +1,163 @@ +import Head from "next/head"; +import { Fragment, useEffect, useState } from "react"; +import Link from "next/link"; +import { ChevronLeftIcon } from "@heroicons/react/24/outline"; +import Skeleton from "react-loading-skeleton"; +import Footer from "../../../components/footer"; +import { getServerSession } from "next-auth"; +import { authOptions } from "../../api/auth/[...nextauth]"; +import Image from "next/image"; +import MobileNav from "../../../components/shared/MobileNav"; + +export async function getServerSideProps(context) { + const session = await getServerSession(context.req, context.res, authOptions); + + return { + props: { + sessions: session, + }, + }; +} + +export default function Recent({ sessions }) { + const [data, setData] = useState(null); + const [page, setPage] = useState(1); + const [nextPage, setNextPage] = useState(true); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(true); + async function getRecent() { + const data = await fetch(`/api/v2/etc/recent/${page}`).then((res) => + res.json() + ); + if (data?.results?.length === 0) { + setNextPage(false); + } else if (data !== null && page > 1) { + setData((prevData) => { + return [...(prevData ?? []), ...data?.results]; + }); + setNextPage(data?.hasNextPage); + } else { + setData(data?.results); + } + setNextPage(data?.hasNextPage); + setLoading(false); + } + getRecent(); + }, [page]); + + useEffect(() => { + function handleScroll() { + if (page > 5 || !nextPage) { + window.removeEventListener("scroll", handleScroll); + return; + } + + if ( + window.innerHeight + window.pageYOffset >= + document.body.offsetHeight - 3 + ) { + setPage((prevPage) => prevPage + 1); + } + } + + window.addEventListener("scroll", handleScroll); + + return () => window.removeEventListener("scroll", handleScroll); + }, [page, nextPage]); + + return ( + <Fragment> + <Head> + <title>Moopa - New Episodes</title> + <meta name="title" content="New Episodes" /> + <meta + name="description" + content="Explore Beloved Classics and Favorites - Dive into a curated collection of timeless anime on Moopa's New Episodes Page. From iconic classics to all-time favorites, experience the stories that have captured hearts worldwide. Start streaming now and relive the magic of anime!" + /> + </Head> + <MobileNav sessions={sessions} /> + <main className="flex flex-col gap-2 items-center min-h-screen w-screen px-2 relative pb-10"> + <div className="z-50 bg-primary pt-5 pb-3 shadow-md shadow-primary w-full fixed px-3"> + <Link href="/en" className="flex gap-2 items-center font-karla"> + <ChevronLeftIcon className="w-5 h-5" /> + <h1 className="text-xl">New Episodes</h1> + </Link> + </div> + <div className="grid grid-cols-2 xs:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-6 gap-5 max-w-6xl pt-20"> + {data?.map((i, index) => ( + <div + key={index} + className="flex flex-col items-center w-[150px] lg:w-[180px]" + > + <Link + href={`/en/anime/${i.id}`} + className=" relative hover:scale-105 scale-100 transition-all duration-200 ease-out" + title={i.title.romaji} + > + <div className="w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] object-cover rounded opacity-90 z-20"> + <div className="absolute bg-gradient-to-b from-black/30 to-transparent from-5% to-30% top-0 z-30 w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] rounded" /> + <Image + src={i.image} + alt={i.title.romaji} + width={500} + height={500} + className="w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] object-cover rounded opacity-90 z-20" + /> + </div> + <Image + src="/svg/episode-badge.svg" + alt="episode-bade" + width={200} + height={100} + className="w-24 lg:w-28 absolute top-1 -right-[13px] lg:-right-[15px] z-40" + /> + <p className="absolute z-40 text-center w-[80px] lg:w-[100px] top-[5px] -right-2 lg:top-[4px] lg:-right-3 font-karla text-sm lg:text-base"> + Episode <span className="text-white">{i?.episodeNumber}</span> + </p> + </Link> + <Link + href={`/en/anime/${i.id}`} + className="w-full px-1 py-2" + title={i.title.romaji} + > + <h1 className="font-karla font-bold xl:text-base text-[15px] line-clamp-2"> + <span className="dots bg-green-500" /> + {i.title.romaji} + </h1> + </Link> + </div> + ))} + + {loading && ( + <> + {[1, 2, 4, 5, 6, 7, 8].map((item) => ( + <div + key={item} + className="flex flex-col items-center w-[150px] lg:w-[180px]" + > + <div className="w-full p-2"> + <Skeleton className="w-[140px] h-[190px] lg:w-[170px] lg:h-[230px] rounded" /> + </div> + <div className="w-full px-2"> + <Skeleton width={80} height={20} /> + </div> + </div> + ))} + </> + )} + </div> + {!loading && page > 5 && nextPage && ( + <button + onClick={() => setPage((p) => p + 1)} + className="bg-secondary xl:w-[30%] w-[80%] h-10 rounded-md" + > + Load More + </button> + )} + </main> + <Footer /> + </Fragment> + ); +} |