diff options
Diffstat (limited to 'pages/en/manga')
| -rw-r--r-- | pages/en/manga/[...id].js | 427 | ||||
| -rw-r--r-- | pages/en/manga/[...id].tsx | 456 | ||||
| -rw-r--r-- | pages/en/manga/read/[...params].js | 1 |
3 files changed, 457 insertions, 427 deletions
diff --git a/pages/en/manga/[...id].js b/pages/en/manga/[...id].js deleted file mode 100644 index 5648b2c..0000000 --- a/pages/en/manga/[...id].js +++ /dev/null @@ -1,427 +0,0 @@ -import ChapterSelector from "@/components/manga/chapters"; -import Footer from "@/components/shared/footer"; -import Head from "next/head"; -import { useEffect, useState } from "react"; -import { getServerSession } from "next-auth"; -import { authOptions } from "../../api/auth/[...nextauth]"; -import { mediaInfoQuery } from "@/lib/graphql/query"; -import Modal from "@/components/modal"; -import { signIn, useSession } from "next-auth/react"; -import AniList from "@/components/media/aniList"; -import ListEditor from "@/components/listEditor"; -import MobileNav from "@/components/shared/MobileNav"; -import Image from "next/image"; -import DetailTop from "@/components/anime/mobile/topSection"; -import Characters from "@/components/anime/charactersCard"; -import Content from "@/components/home/content"; -import { toast } from "sonner"; -import axios from "axios"; -import getAnifyInfo from "@/lib/anify/info"; -import { redis } from "@/lib/redis"; -import getMangaId from "@/lib/anify/getMangaId"; - -export default function Manga({ info, anifyData, color, chapterNotFound }) { - const [domainUrl, setDomainUrl] = useState(""); - const { data: session } = useSession(); - - const [loading, setLoading] = useState(false); - const [progress, setProgress] = useState(0); - const [statuses, setStatuses] = useState(null); - const [watch, setWatch] = useState(); - - const [chapter, setChapter] = useState(null); - - const [open, setOpen] = useState(false); - - const rec = info?.recommendations?.nodes?.map( - (data) => data.mediaRecommendation - ); - - useEffect(() => { - setDomainUrl(window.location.origin); - }, []); - - useEffect(() => { - if (chapterNotFound) { - toast.error("Chapter not found"); - const cleanUrl = window.location.origin + window.location.pathname; - window.history.replaceState(null, null, cleanUrl); - } - }, [chapterNotFound]); - - useEffect(() => { - async function fetchData() { - try { - setLoading(true); - - const { data } = await axios.get(`/api/v2/info?id=${anifyData.id}`); - - if (!data.chapters) { - setLoading(false); - return; - } - - setChapter(data); - setLoading(false); - } catch (error) { - console.error(error); - } - } - fetchData(); - - return () => { - setChapter(null); - }; - }, [info?.id]); - - function handleOpen() { - setOpen(true); - document.body.style.overflow = "hidden"; - } - - function handleClose() { - setOpen(false); - document.body.style.overflow = "auto"; - } - - return ( - <> - <Head> - <title> - {info - ? `Manga - ${ - info.title.romaji || info.title.english || info.title.native - }` - : "Getting Info..."} - </title> - <meta name="twitter:card" content="summary_large_image" /> - <meta - name="twitter:title" - content={`Moopa - ${info.title.romaji || info.title.english}`} - /> - <meta - name="twitter:description" - content={`${info.description?.slice(0, 180)}...`} - /> - <meta - name="twitter:image" - content={`${domainUrl}/api/og?title=${ - info.title.romaji || info.title.english - }&image=${info.bannerImage || info.coverImage}`} - /> - <meta - name="title" - data-title-romaji={info?.title?.romaji} - data-title-english={info?.title?.english} - data-title-native={info?.title?.native} - /> - </Head> - <Modal open={open} onClose={() => handleClose()}> - <div> - {!session && ( - <div className="flex-center flex-col gap-5 px-10 py-5 bg-secondary rounded-md"> - <div className="text-md font-extrabold font-karla"> - Edit your list - </div> - <button - className="flex items-center bg-[#363642] rounded-md text-white p-1" - onClick={() => signIn("AniListProvider")} - > - <h1 className="px-1 font-bold font-karla"> - Login with AniList - </h1> - <div className="scale-[60%] pb-[1px]"> - <AniList /> - </div> - </button> - </div> - )} - {session && info && ( - <ListEditor - animeId={info?.id} - session={session} - stats={statuses?.value} - prg={progress} - max={info?.episodes} - info={info} - close={handleClose} - /> - )} - </div> - </Modal> - <MobileNav sessions={session} hideProfile={true} /> - <main className="w-screen min-h-screen overflow-hidden relative flex flex-col items-center gap-5"> - {/* <div className="absolute bg-gradient-to-t from-primary from-85% to-100% to-transparent w-screen h-full z-10" /> */} - <div className="w-screen absolute"> - <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[280px] w-screen z-10 inset-0" /> - {info?.bannerImage && ( - <Image - src={info?.bannerImage} - alt="banner anime" - height={1000} - width={1000} - blurDataURL={info?.bannerImage} - className="object-cover bg-image blur-[2px] w-screen absolute top-0 left-0 h-[250px] brightness-[55%] z-0" - /> - )} - </div> - <div className="w-full lg:max-w-screen-lg xl:max-w-screen-2xl z-30 flex flex-col gap-5 pb-10"> - <DetailTop - info={info} - session={session} - handleOpen={handleOpen} - loading={loading} - statuses={statuses} - watchUrl={watch} - progress={progress} - color={color} - /> - - {!loading ? ( - chapter?.chapters?.length > 0 ? ( - <ChapterSelector - chaptersData={chapter.chapters} - mangaId={chapter.id} - data={info} - setWatch={setWatch} - /> - ) : ( - <div className="h-[20vh] lg:w-full flex-center flex-col gap-5"> - <p className="text-center font-karla font-bold lg:text-lg"> - Oops!<br></br> It looks like this manga is not available. - </p> - </div> - ) - ) : ( - <div className="flex justify-center"> - <div className="lds-ellipsis"> - <div></div> - <div></div> - <div></div> - <div></div> - </div> - </div> - )} - - {info?.characters?.edges?.length > 0 && ( - <div className="w-full"> - <Characters info={info?.characters?.edges} /> - </div> - )} - - {info && rec && rec?.length !== 0 && ( - <div className="w-full"> - <Content - ids="recommendAnime" - section="Recommendations" - type="manga" - data={rec} - /> - </div> - )} - </div> - </main> - <Footer /> - </> - ); -} - -export async function getServerSideProps(context) { - const session = await getServerSession(context.req, context.res, authOptions); - const accessToken = session?.user?.token || null; - - const { chapter } = context.query; - const [id1, id2] = context.query.id; - - let cached; - let aniId, mangadexId; - let info, data, color, chapterNotFound; - - if (String(id1).length > 6) { - aniId = id2; - mangadexId = id1; - } else { - aniId = id1; - mangadexId = id2; - } - - if (chapter) { - // create random id string - chapterNotFound = Math.random().toString(36).substring(7); - } - - if (aniId === "na" && mangadexId) { - const datas = await getAnifyInfo(mangadexId); - - aniId = - datas.mappings?.filter((i) => i.providerId === "anilist")[0]?.id || null; - - if (!aniId) { - info = datas; - data = datas; - color = { - backgroundColor: `${"#ffff"}`, - color: "#000", - }; - // return { - // redirect: { - // destination: "/404", - // permanent: false, - // }, - // }; - } - } else if (aniId && !mangadexId) { - // console.log({ aniId }); - const response = await fetch("https://graphql.anilist.co/", { - method: "POST", - headers: { - "Content-Type": "application/json", - ...(accessToken && { Authorization: `Bearer ${accessToken}` }), - }, - body: JSON.stringify({ - query: `query ($id: Int, $type: MediaType) { - Media (id: $id, type: $type) { - id - title { - romaji - english - native - } - } - }`, - variables: { - id: parseInt(aniId), - type: "MANGA", - }, - }), - }); - const aniListData = await response.json(); - const info = aniListData?.data?.Media; - - const mangaId = await getMangaId( - info?.title?.romaji, - info?.title?.english, - info?.title?.native - ); - mangadexId = mangaId?.id; - - if (!mangadexId) { - return { - redirect: { - destination: "/404", - permanent: false, - }, - }; - } - - return { - redirect: { - destination: `/en/manga/${aniId}/${mangadexId}${ - chapter ? "?chapter=404" : "" - }`, - permanent: true, - }, - }; - } else if (!aniId && mangadexId) { - const data = await getAnifyInfo(mangadexId); - - aniId = - data.mappings.filter((i) => i.providerId === "anilist")[0]?.id || null; - - if (!aniId) { - info = data; - // return { - // redirect: { - // destination: "/404", - // permanent: false, - // }, - // }; - } - - return { - redirect: { - destination: `/en/manga/${aniId ? aniId : "na"}${`/${mangadexId}`}${ - chapter ? "?chapter=404" : "" - }`, - permanent: true, - }, - }; - } else { - if (redis) { - const getCached = await redis.get(`mangaPage:${mangadexId}`); - - if (getCached) { - cached = JSON.parse(getCached); - } - } - // let chapters; - if (cached) { - data = cached.data; - info = cached.info; - color = cached.color; - } else { - data = await getAnifyInfo(mangadexId); - - const aniListId = - data.mappings?.filter((i) => i.providerId === "anilist")[0]?.id || null; - - const response = await fetch("https://graphql.anilist.co/", { - method: "POST", - headers: { - "Content-Type": "application/json", - ...(accessToken && { Authorization: `Bearer ${accessToken}` }), - }, - body: JSON.stringify({ - query: mediaInfoQuery, - variables: { - id: parseInt(aniListId), - type: "MANGA", - }, - }), - }); - const aniListData = await response.json(); - if (aniListData?.data?.Media) info = aniListData?.data?.Media; - - const textColor = setTxtColor(info?.color); - - color = { - backgroundColor: `${info?.color || "#ffff"}`, - color: textColor, - }; - - if (redis) { - await redis.set( - `mangaPage:${mangadexId}`, - JSON.stringify({ data, info, color }), - "ex", - 60 * 60 * 24 - ); - } - } - } - - return { - props: { - info: info || null, - anifyData: data || null, - chapterNotFound: chapterNotFound || null, - color: color || null, - }, - }; -} - -function getBrightness(hexColor) { - if (!hexColor) { - return 200; - } - const rgb = hexColor - .match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i) - .slice(1) - .map((x) => parseInt(x, 16)); - return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000; -} - -function setTxtColor(hexColor) { - const brightness = getBrightness(hexColor); - return brightness < 150 ? "#fff" : "#000"; -} diff --git a/pages/en/manga/[...id].tsx b/pages/en/manga/[...id].tsx new file mode 100644 index 0000000..d1c10a4 --- /dev/null +++ b/pages/en/manga/[...id].tsx @@ -0,0 +1,456 @@ +import Footer from "@/components/shared/footer"; +import Head from "next/head"; +import { useEffect, useState } from "react"; +import { getServerSession } from "next-auth"; +import { authOptions } from "../../api/auth/[...nextauth]"; +import { mediaInfoQuery } from "@/lib/graphql/query"; +import Modal from "@/components/modal"; +import { signIn } from "next-auth/react"; +import AniList from "@/components/media/aniList"; +import ListEditor from "@/components/listEditor"; +import MobileNav from "@/components/shared/MobileNav"; +import Image from "next/image"; +import DetailTop from "@/components/anime/mobile/topSection"; +import Characters from "@/components/anime/charactersCard"; +import Content from "@/components/home/content"; +import { toast } from "sonner"; +import getAnifyInfo from "@/lib/anify/info"; +import getMangaId from "@/lib/anify/getMangaId"; +import { useRouter } from "next/router"; +import ChaptersComponent from "@/components/manga/ChaptersComponent"; +import pls from "@/utils/request/index"; +import { AniListInfoTypes } from "types/info/AnilistInfoTypes"; +import { Navbar } from "@/components/shared/NavBar"; + +type MangaProps = { + aniId: string; + mangadexId: string; + sessions: any; + metaData: any; + chapterNotFound: string; +}; + +export default function Manga({ + aniId, + mangadexId, + sessions: session, + chapterNotFound, + metaData, +}: MangaProps) { + const [domainUrl, setDomainUrl] = useState(""); + + const [loading, setLoading] = useState(false); + const [watch, setWatch] = useState(); + + const [mangaId, setMangaId] = useState<string | null>(mangadexId); + const [chapters, setChapters] = useState(null); + const [notFound, setNotFound] = useState(false); + + const [info, setInfo] = useState<AniListInfoTypes | null>(null); + const [color, setColor] = useState(null); + + const [open, setOpen] = useState(false); + + const router = useRouter(); + + const rec = info?.recommendations?.nodes?.map( + (data) => data.mediaRecommendation + ); + + useEffect(() => { + setDomainUrl(window.location.origin); + }, []); + + useEffect(() => { + if (chapterNotFound) { + toast.error("Chapter not found"); + const cleanUrl = window.location.origin + window.location.pathname; + window.history.replaceState(null, "", cleanUrl); + } + }, [chapterNotFound]); + + useEffect(() => { + setMangaId(null); + }, [aniId]); + + useEffect(() => { + async function fetchData() { + try { + let info, data, color: any; + setChapters(null); + setNotFound(false); + + if (aniId && mangadexId) { + const [aniListData] = await pls.post("https://graphql.anilist.co/", { + body: JSON.stringify({ + query: mediaInfoQuery, + variables: { + id: parseInt(aniId), + type: "MANGA", + }, + }), + }); + // const aniListData = await response.json(); + info = aniListData?.data?.Media; + const textColor = setTxtColor(info?.color); + + color = { + backgroundColor: `${info?.color || "#ffff"}`, + color: textColor, + }; + + setInfo(info); + setColor(color); + setMangaId(mangadexId); + // console.log("wow two of them here"); + } else if (aniId && !mangadexId) { + const [aniListData] = await pls.post("https://graphql.anilist.co/", { + body: JSON.stringify({ + query: mediaInfoQuery, + variables: { + id: parseInt(aniId), + type: "MANGA", + }, + }), + }); + // const aniListData = await response.json(); + info = aniListData?.data?.Media; + const textColor = setTxtColor(info?.color); + + color = { + backgroundColor: `${info?.color || "#ffff"}`, + color: textColor, + }; + + setInfo(info); + setColor(color); + + const mangaId = await getMangaId( + info?.title?.romaji, + info?.title?.english, + info?.title?.native + ); + + mangadexId = (mangaId as { id: string }).id; + + if (mangadexId) { + setMangaId(mangadexId); + // console.log("mangadex is here", mangadexId); + router.push("/en/manga/" + aniId + "/" + mangadexId, undefined, { + shallow: true, + }); + } else { + // console.log("why is this running?"); + setMangaId(null); + setLoading(false); + setNotFound(true); + // router.push("/en/manga/" + aniId, undefined, { shallow: true }); + } + } else if (!aniId && mangadexId) { + data = await getAnifyInfo(mangadexId); + + const aniListId = + data.mappings?.filter((i: any) => i.providerId === "anilist")[0] + ?.id || null; + + if (aniListId) { + const [aniListData] = await pls.post( + "https://graphql.anilist.co/", + { + body: JSON.stringify({ + query: mediaInfoQuery, + variables: { + id: parseInt(aniListId), + type: "MANGA", + }, + }), + } + ); + // const aniListData = await response.json(); + info = aniListData?.data?.Media; + + router.push( + "/en/manga/" + aniListId + "/" + mangadexId, + undefined, + { shallow: true } + ); + } + + const textColor = setTxtColor(data?.color); + + color = { + backgroundColor: `${data?.color || "#ffff"}`, + color: textColor, + }; + + setInfo(aniListId ? info : data); + setColor(color); + setMangaId(mangadexId); + } + } catch (error) { + console.log(error); + } + } + fetchData(); + + return () => { + setInfo(null); + }; + }, [session?.user?.token, aniId, mangadexId]); + + function handleOpen() { + setOpen(true); + document.body.style.overflow = "hidden"; + } + + function handleClose() { + setOpen(false); + document.body.style.overflow = "auto"; + } + + return ( + <> + <Head> + <title> + {metaData + ? `Manga - ${ + metaData.title.romaji || + metaData.title.english || + metaData.title.native + }` + : "Getting Info..."} + </title> + <meta + name="description" + content={`${metaData?.description?.slice(0, 180)}...`} + /> + <meta + name="keywords" + content={`${metaData?.genres}, ${metaData?.author} `} + /> + <meta + property="og:title" + content={`Moopa - ${ + metaData?.title.romaji || metaData?.title.english + }`} + /> + <meta + property="og:description" + content={`${metaData?.description?.slice(0, 180)}...`} + /> + <meta + property="og:image" + content={`${domainUrl}/api/og?title=${ + metaData?.title.romaji || metaData?.title.english + }&image=${metaData?.bannerImage || metaData?.coverImage}`} + /> + <meta + property="og:url" + content={`${domainUrl}/en/manga/${metaData?.id}`} + /> + <meta property="og:type" content="book" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image" /> + <meta name="twitter:site" content="@yourTwitterHandle" /> + <meta + name="twitter:title" + content={`Moopa - ${ + metaData?.title.romaji || metaData?.title.english + }`} + /> + <meta + name="twitter:description" + content={`${metaData?.description?.slice(0, 180)}...`} + /> + <meta name="robots" content="noindex" /> + <meta + name="twitter:image" + content={`${domainUrl}/api/og?title=${ + metaData?.title.romaji || metaData?.title.english + }&image=${metaData?.bannerImage || metaData?.coverImage}`} + /> + </Head> + <Navbar info={info} manga /> + <Modal open={open} onClose={() => handleClose()}> + <div> + {!session && ( + <div className="flex-center flex-col gap-5 px-10 py-5 bg-secondary rounded-md"> + <div className="text-md font-extrabold font-karla"> + Edit your list + </div> + <button + className="flex items-center bg-[#363642] rounded-md text-white p-1" + onClick={() => signIn("AniListProvider")} + > + <h1 className="px-1 font-bold font-karla"> + Login with AniList + </h1> + <div className="scale-[60%] pb-[1px]"> + <AniList /> + </div> + </button> + </div> + )} + {session && info && ( + <ListEditor + animeId={info?.id} + session={session} + // stats={statuses?.value} + // prg={progress} + max={info?.episodes} + info={info} + close={handleClose} + /> + )} + </div> + </Modal> + <MobileNav hideProfile={true} /> + <main className="w-screen min-h-screen overflow-hidden relative flex flex-col items-center gap-5"> + <div className="w-screen absolute"> + <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[280px] w-screen z-10 inset-0" /> + {info?.bannerImage && ( + <Image + src={info?.bannerImage} + alt="banner anime" + height={1000} + width={1000} + blurDataURL={info?.bannerImage} + className="object-cover bg-image blur-[2px] w-screen absolute top-0 left-0 h-[250px] brightness-[55%] z-0" + /> + )} + </div> + <div className="w-full lg:max-w-screen-lg xl:max-w-screen-2xl z-30 flex flex-col gap-5 pb-10"> + {/* {info && ( */} + <DetailTop + info={info} + handleOpen={handleOpen} + // statuses={statuses} + watchUrl={watch} + // progress={progress} + color={color} + /> + {/* )} */} + + <ChaptersComponent + info={info} + mangaId={mangaId} + aniId={aniId} + setWatch={setWatch} + chapter={chapters} + setChapter={setChapters} + loading={loading} + setLoading={setLoading} + notFound={notFound} + setNotFound={setNotFound} + /> + + {info && info.characters.edges.length > 0 && ( + <div className="w-full"> + <Characters info={info?.characters?.edges} /> + </div> + )} + + {info && rec && rec?.length !== 0 && ( + <div className="w-full"> + <Content + ids="recommendAnime" + section="Recommendations" + type="manga" + data={rec} + /> + </div> + )} + </div> + </main> + <Footer /> + </> + ); +} + +export async function getServerSideProps(context: any) { + const session: any = await getServerSession( + context.req, + context.res, + authOptions + ); + const accessToken = session?.user?.token || null; + + const { chapter } = context.query; + const [id1, id2] = context.query.id; + + let aniId, mangadexId; + let chapterNotFound; + + if (String(id1).length > 6) { + aniId = id2; + mangadexId = id1; + } else { + aniId = id1; + mangadexId = id2; + } + + if (chapter) { + // create random id string + chapterNotFound = Math.random().toString(36).substring(7); + } + + const [aniListData] = await pls.post("https://graphql.anilist.co/", { + body: JSON.stringify({ + query: `query ($id: Int, $type: MediaType) { + Media(id: $id, type: $type) { + id + title { + romaji + english + native + } + bannerImage + genres + coverImage { + extraLarge + large + medium + color + } + status + description + } + }`, + variables: { + id: parseInt(aniId), + type: "MANGA", + }, + }), + }); + const info = aniListData?.data?.Media; + + return { + props: { + aniId: aniId || null, + mangadexId: mangadexId || null, + accessToken: accessToken || null, + sessions: session || null, + metaData: info || null, + // info: info || null, + // anifyData: data || null, + chapterNotFound: chapterNotFound || null, + // color: color || null, + }, + }; +} + +function getBrightness(hexColor: { match: (arg0: RegExp) => any[] }) { + if (!hexColor) { + return 200; + } + const rgb = hexColor + .match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i) + .slice(1) + .map((x) => parseInt(x, 16)); + return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000; +} + +function setTxtColor(hexColor: { match: (arg0: RegExp) => any[] }) { + const brightness = getBrightness(hexColor); + return brightness < 150 ? "#fff" : "#000"; +} diff --git a/pages/en/manga/read/[...params].js b/pages/en/manga/read/[...params].js index a7fa78b..036b999 100644 --- a/pages/en/manga/read/[...params].js +++ b/pages/en/manga/read/[...params].js @@ -150,6 +150,7 @@ export default function Read({ data-title-native={info?.title?.native} /> <meta id="CoverImage" data-manga-cover={info?.coverImage} /> + <meta name="robots" content="noindex" /> </Head> <div className="w-screen flex justify-evenly relative"> <ShortCutModal isOpen={isKeyOpen} setIsOpen={setIsKeyOpen} /> |