aboutsummaryrefslogtreecommitdiff
path: root/pages/en
diff options
context:
space:
mode:
Diffstat (limited to 'pages/en')
-rw-r--r--pages/en/about.tsx (renamed from pages/en/about.js)4
-rw-r--r--pages/en/anime/[...id].tsx (renamed from pages/en/anime/[...id].js)79
-rw-r--r--pages/en/anime/recent.js2
-rw-r--r--pages/en/anime/watch/[...info].js213
-rw-r--r--pages/en/contact.tsx (renamed from pages/en/contact.js)4
-rw-r--r--pages/en/dmca.tsx (renamed from pages/en/dmca.js)4
-rw-r--r--pages/en/index.tsx (renamed from pages/en/index.js)242
-rw-r--r--pages/en/manga/[...id].js427
-rw-r--r--pages/en/manga/[...id].tsx456
-rw-r--r--pages/en/manga/read/[...params].js1
-rw-r--r--pages/en/profile/[user].tsx (renamed from pages/en/profile/[user].js)83
-rw-r--r--pages/en/schedule/index.tsx (renamed from pages/en/schedule/index.js)35
-rw-r--r--pages/en/search/[...param].tsx (renamed from pages/en/search/[...param].js)211
13 files changed, 1029 insertions, 732 deletions
diff --git a/pages/en/about.js b/pages/en/about.tsx
index aa0ba30..c5e9c51 100644
--- a/pages/en/about.js
+++ b/pages/en/about.tsx
@@ -1,7 +1,7 @@
import Head from "next/head";
import { motion } from "framer-motion";
import Link from "next/link";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import Footer from "@/components/shared/footer";
export default function About() {
@@ -21,7 +21,7 @@ export default function About() {
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/svg/c.svg" />
</Head>
- <NewNavbar withNav={true} scrollP={5} shrink={true} />
+ <Navbar withNav={true} scrollP={5} shrink={true} />
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].tsx
index 25cc4d6..42cae38 100644
--- a/pages/en/anime/[...id].js
+++ b/pages/en/anime/[...id].tsx
@@ -16,18 +16,30 @@ import Footer from "@/components/shared/footer";
import { mediaInfoQuery } from "@/lib/graphql/query";
import MobileNav from "@/components/shared/MobileNav";
+import pls from "@/utils/request/index";
+
import Characters from "@/components/anime/charactersCard";
import { redis } from "@/lib/redis";
-
-export default function Info({ info, color }) {
- const { data: session } = useSession();
+import { toast } from "sonner";
+import { Navbar } from "@/components/shared/NavBar";
+import { AniListInfoTypes } from "types/info/AnilistInfoTypes";
+
+type InfoTypes = {
+ info: AniListInfoTypes;
+ color: string;
+ api: string;
+ chapterNotFound: string;
+};
+
+export default function Info({ info, color, chapterNotFound }: InfoTypes) {
+ const { data: session }: any = useSession();
const { getUserLists } = useAniList(session);
const [loading, setLoading] = useState(false);
- const [progress, setProgress] = useState(0);
- const [statuses, setStatuses] = useState(null);
+ const [progress, setProgress] = useState<number>(0);
+ const [statuses, setStatuses] = useState<any>(null);
const [domainUrl, setDomainUrl] = useState("");
- const [watch, setWatch] = useState();
+ const [watch, setWatch] = useState<string>();
const [open, setOpen] = useState(false);
const { id } = useRouter().query;
@@ -37,6 +49,14 @@ export default function Info({ info, color }) {
);
useEffect(() => {
+ if (chapterNotFound) {
+ toast.error("Source not found");
+ const cleanUrl = window.location.origin + window.location.pathname;
+ window.history.replaceState(null, "", cleanUrl);
+ }
+ }, [chapterNotFound]);
+
+ useEffect(() => {
handleClose();
async function fetchData() {
setLoading(true);
@@ -53,7 +73,9 @@ export default function Info({ info, color }) {
if (user) {
setProgress(user.progress);
- const statusMapping = {
+ const statusMapping: {
+ [key: string]: { name: string; value: string };
+ } = {
CURRENT: { name: "Watching", value: "CURRENT" },
PLANNING: { name: "Plan to watch", value: "PLANNING" },
COMPLETED: { name: "Completed", value: "COMPLETED" },
@@ -118,6 +140,7 @@ export default function Info({ info, color }) {
}&image=${info.bannerImage || info.coverImage.extraLarge}`}
/>
</Head>
+ <Navbar info={info} />
<Modal open={open} onClose={() => handleClose()}>
<div>
{!session && (
@@ -151,7 +174,7 @@ export default function Info({ info, color }) {
)}
</div>
</Modal>
- <MobileNav sessions={session} hideProfile={true} />
+ <MobileNav hideProfile={true} />
<main className="w-screen min-h-screen relative flex flex-col items-center bg-primary 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" />
@@ -169,12 +192,10 @@ export default function Info({ info, color }) {
<div className="w-full lg:max-w-screen-lg xl:max-w-screen-2xl z-30 flex flex-col gap-5">
<DetailTop
info={info}
- session={session}
handleOpen={handleOpen}
- loading={loading}
statuses={statuses}
watchUrl={watch}
- progress={progress}
+ progress={progress || 0}
color={color}
/>
@@ -188,6 +209,9 @@ export default function Info({ info, color }) {
{info?.characters?.edges && (
<div className="w-full">
+ {/* <div className="w-full h-[150px] bg-white flex-center text-black">
+ ad banner
+ </div> */}
<Characters info={info?.characters?.edges} />
</div>
)}
@@ -208,8 +232,8 @@ export default function Info({ info, color }) {
);
}
-export async function getServerSideProps(ctx) {
- const { id } = ctx.query;
+export async function getServerSideProps(ctx: any) {
+ const { id, notfound } = ctx.query;
let API_URI;
API_URI = process.env.API_URI || null || null;
@@ -217,7 +241,12 @@ export async function getServerSideProps(ctx) {
API_URI = API_URI.slice(0, -1);
}
- let cache;
+ let cache, chapterNotFound;
+
+ if (notfound) {
+ // create random id string
+ chapterNotFound = Math.random().toString(36).substring(7);
+ }
if (redis) {
cache = await redis.get(`anime:${id}`);
@@ -230,14 +259,15 @@ export async function getServerSideProps(ctx) {
info,
color,
api: API_URI,
+ chapterNotFound: chapterNotFound || null,
},
};
} else {
- const resp = await fetch("https://graphql.anilist.co/", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
+ const [resp] = await pls.post("https://graphql.anilist.co/", {
+ // method: "POST",
+ // headers: {
+ // "Content-Type": "application/json",
+ // },
body: JSON.stringify({
query: mediaInfoQuery,
variables: {
@@ -246,10 +276,10 @@ export async function getServerSideProps(ctx) {
}),
});
- const json = await resp.json();
- const data = json?.data?.Media;
+ // const json = await resp.json();
+ const data = resp?.data?.Media;
- const cacheTime = data.nextAiringEpisode?.episode
+ const cacheTime = data?.nextAiringEpisode?.episode
? 60 * 10
: 60 * 60 * 24 * 30;
@@ -283,12 +313,13 @@ export async function getServerSideProps(ctx) {
info: data,
color: color,
api: API_URI,
+ chapterNotFound: chapterNotFound || null,
},
};
}
}
-function getBrightness(hexColor) {
+function getBrightness(hexColor: { match: (arg0: RegExp) => any[] }) {
if (!hexColor) {
return 200;
}
@@ -299,7 +330,7 @@ function getBrightness(hexColor) {
return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000;
}
-function setTxtColor(hexColor) {
+function setTxtColor(hexColor: { match: (arg0: RegExp) => any[] }) {
const brightness = getBrightness(hexColor);
return brightness < 150 ? "#fff" : "#000";
}
diff --git a/pages/en/anime/recent.js b/pages/en/anime/recent.js
index 4a8111d..240ed1d 100644
--- a/pages/en/anime/recent.js
+++ b/pages/en/anime/recent.js
@@ -83,7 +83,7 @@ export default function Recent({ sessions }) {
<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>
+ <h1 className="text-xl">Freshly Added</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">
diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js
index beab366..dc1f412 100644
--- a/pages/en/anime/watch/[...info].js
+++ b/pages/en/anime/watch/[...info].js
@@ -1,5 +1,4 @@
-import React, { useEffect, useRef, useState } from "react";
-import PlayerComponent from "@/components/watch/player/playerComponent";
+import { useEffect, useState } from "react";
import { FlagIcon, ShareIcon } from "@heroicons/react/24/solid";
import Details from "@/components/watch/primary/details";
import EpisodeLists from "@/components/watch/secondary/episodeLists";
@@ -9,13 +8,16 @@ import { authOptions } from "../../../api/auth/[...nextauth]";
import { createList, createUser, getEpisode } from "@/prisma/user";
import Link from "next/link";
import MobileNav from "@/components/shared/MobileNav";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import Modal from "@/components/modal";
import AniList from "@/components/media/aniList";
import { signIn } from "next-auth/react";
import BugReportForm from "@/components/shared/bugReport";
import Skeleton from "react-loading-skeleton";
import Head from "next/head";
+import VidStack from "@/components/watch/new-player/player";
+import { useRouter } from "next/router";
+import { Spinner } from "@vidstack/react";
export async function getServerSideProps(context) {
let userData = null;
@@ -81,7 +83,7 @@ export async function getServerSideProps(context) {
color
}
synonyms
-
+
}
}
`,
@@ -91,6 +93,8 @@ export async function getServerSideProps(context) {
}),
});
const data = await ress.json();
+ // const variables = { id: aniId };
+ // const data = await getAnilistMediaInfo(variables, context.req);
try {
if (session) {
@@ -142,17 +146,24 @@ export default function Watch({
const [episodesList, setepisodesList] = useState();
const [mapEpisode, setMapEpisode] = useState(null);
- const [episodeSource, setEpisodeSource] = useState(null);
-
const [open, setOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false);
+ const { setAutoNext } = useWatchProvider();
+
const [onList, setOnList] = useState(false);
- const { theaterMode, setPlayerState, setAutoPlay, setMarked } =
- useWatchProvider();
+ const router = useRouter();
- const playerRef = useRef(null);
+ const {
+ theaterMode,
+ setPlayerState,
+ setAutoPlay,
+ setMarked,
+ setTrack,
+ aspectRatio,
+ setDataMedia,
+ } = useWatchProvider();
useEffect(() => {
async function getInfo() {
@@ -160,6 +171,8 @@ export default function Watch({
setOnList(true);
}
+ setDataMedia(info);
+
const response = await fetch(
`/api/v2/episode/${info.id}?releasing=${
info.status === "RELEASING" ? "true" : "false"
@@ -202,17 +215,18 @@ export default function Watch({
const previousEpisode = episodeList?.find(
(i) => i.number === parseInt(epiNumber) - 1
);
- setEpisodeNavigation({
+ const vidNav = {
prev: previousEpisode,
playing: {
id: currentEpisode.id,
- title: playingData?.title,
+ title: playingData?.title || info?.title?.romaji,
description: playingData?.description,
img: playingData?.img || playingData?.image,
number: currentEpisode.number,
},
next: nextEpisode,
- });
+ };
+ setEpisodeNavigation(vidNav);
}
}
@@ -228,12 +242,17 @@ export default function Watch({
}, [sessions?.user?.name, epiNumber, dub]);
useEffect(() => {
+ const autoNext = localStorage.getItem("autoNext"),
+ autoPlay = localStorage.getItem("autoplay");
+ if (autoNext) {
+ setAutoNext(autoNext);
+ }
+ if (autoPlay) {
+ setAutoPlay(autoPlay);
+ }
+
async function fetchData() {
if (info) {
- const autoplay =
- localStorage.getItem("autoplay_video") === "true" ? true : false;
- setAutoPlay(autoplay);
-
const anify = await fetch("/api/v2/source", {
method: "POST",
headers: {
@@ -252,6 +271,11 @@ export default function Watch({
}),
}).then((res) => res.json());
+ if (!anify?.sources?.length > 0) {
+ router.push(`/en/anime/${info.id}?notfound=true`);
+ return;
+ }
+
const skip = await fetch(
`https://api.aniskip.com/v2/skip-times/${info.idMal}/${parseInt(
epiNumber
@@ -267,31 +291,77 @@ export default function Watch({
return res.json();
});
- const op =
- skip?.results?.find((item) => item.skipType === "op") || null;
- const ed =
- skip?.results?.find((item) => item.skipType === "ed") || null;
+ let getOp =
+ skip?.results?.find((item) => item.skipType === "op") || null,
+ getEd = skip?.results?.find((item) => item.skipType === "ed") || null;
+
+ const op = getOp
+ ? {
+ startTime:
+ anify?.intro?.start ?? Math.round(getOp?.interval.startTime),
+ endTime:
+ anify?.intro?.end ?? Math.round(getOp?.interval.endTime),
+ text: "Opening",
+ }
+ : null,
+ ed = {
+ startTime:
+ anify?.outro?.start ?? Math.round(getEd?.interval.startTime),
+ endTime: anify?.outro?.end ?? Math.round(getEd?.interval.endTime),
+ text: "Ending",
+ };
+ const skipData = [op, ed].filter((i) => i !== null);
+
+ const quality =
+ anify?.sources?.find(
+ (i) => i.quality === "default" || i.quality === "auto"
+ ) || anify?.sources[0];
+
+ const reFormSubtitles = anify?.subtitles?.map((i) => {
+ return {
+ src: proxy + "/" + i.url,
+ label: i.lang,
+ kind: i.lang === "Thumbnails" ? "thumbnails" : "subtitles",
+ ...(i.lang === "English" && { default: true }),
+ };
+ });
+
+ const thumbnails = reFormSubtitles?.find(
+ (i) => i.kind === "thumbnails"
+ );
+
+ const subtitles = reFormSubtitles?.filter(
+ (i) => i.kind !== "thumbnails"
+ );
const episode = {
- epiData: anify,
- skip: {
- op,
- ed,
+ provider,
+ isDub: dub,
+ defaultQuality: {
+ // url: quality?.url,
+ url: `${proxy}/proxy/m3u8/${encodeURIComponent(
+ String(quality?.url)
+ )}/${encodeURIComponent(JSON.stringify(anify?.headers))}`,
+ headers: anify?.headers,
},
+ subtitles: subtitles,
+ thumbnails: thumbnails?.src,
+ epiData: anify,
+ skip: skipData,
};
- setEpisodeSource(episode);
+ setTrack(episode);
}
}
fetchData();
return () => {
- setEpisodeSource();
setPlayerState({
currentTime: 0,
isPlaying: false,
});
setMarked(0);
+ setTrack(null);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -424,7 +494,7 @@ export default function Watch({
</Modal>
<BugReportForm isOpen={isOpen} setIsOpen={setIsOpen} />
<main className="w-screen h-full">
- <NewNavbar
+ <Navbar
scrollP={20}
withNav={true}
shrink={true}
@@ -435,21 +505,23 @@ export default function Watch({
className={`mx-auto pt-16 ${theaterMode ? "lg:pt-16" : "lg:pt-20"}`}
>
{theaterMode && (
- <PlayerComponent
- id={"cinematic"}
- session={sessions}
- playerRef={playerRef}
- dub={dub}
- info={info}
- watchId={watchId}
- proxy={proxy}
- track={episodeNavigation}
- data={episodeSource?.epiData}
- skip={episodeSource?.skip}
- timeWatched={userData?.timeWatched}
- provider={provider}
- className="w-screen max-h-[85dvh]"
- />
+ <div
+ className={`bg-black w-full max-h-[84dvh] h-full flex-center rounded-md`}
+ style={{ aspectRatio: aspectRatio }}
+ >
+ {episodeNavigation ? (
+ <VidStack
+ id={`${watchId}-theater`}
+ navigation={episodeNavigation}
+ sessions={sessions}
+ userData={userData}
+ />
+ ) : (
+ <div className="flex-center aspect-video w-full h-full relative">
+ <SpinLoader />
+ </div>
+ )}
+ </div>
)}
<div
id="default"
@@ -459,20 +531,25 @@ export default function Watch({
>
<div id="primary" className="w-full">
{!theaterMode && (
- <PlayerComponent
- id={"default"}
- session={sessions}
- playerRef={playerRef}
- dub={dub}
- info={info}
- watchId={watchId}
- proxy={proxy}
- track={episodeNavigation}
- data={episodeSource?.epiData}
- skip={episodeSource?.skip}
- timeWatched={userData?.timeWatched}
- provider={provider}
- />
+ <div
+ className={`bg-black w-full flex-center rounded-md overflow-hidden ${
+ aspectRatio === "4/3" ? "aspect-video" : ""
+ }`}
+ // style={{ aspectRatio: aspectRatio }}
+ >
+ {episodeNavigation ? (
+ <VidStack
+ id={`${watchId}-default`}
+ navigation={episodeNavigation}
+ sessions={sessions}
+ userData={userData}
+ />
+ ) : (
+ <div className="flex-center aspect-video w-full h-full relative">
+ <SpinLoader />
+ </div>
+ )}
+ </div>
)}
<div
id="details"
@@ -506,7 +583,7 @@ export default function Watch({
className="flex items-center gap-2 px-3 py-1 ring-[1px] ring-white/20 rounded overflow-hidden"
>
<ShareIcon className="w-5 h-5" />
- share
+ <span className="hidden lg:block">share</span>
</button>
<button
type="button"
@@ -514,11 +591,10 @@ export default function Watch({
className="flex items-center gap-2 px-3 py-1 ring-[1px] ring-white/20 rounded overflow-hidden"
>
<FlagIcon className="w-5 h-5" />
- report
+ <span className="hidden lg:block">report</span>
</button>
</div>
</div>
- {/* <div>right</div> */}
</div>
<Details
@@ -538,6 +614,11 @@ export default function Watch({
id="secondary"
className={`relative ${theaterMode ? "pt-5" : "pt-4 lg:pt-0"}`}
>
+ {/* <div className="w-full h-[150px] text-black p-3">
+ <span className="bg-white w-full h-full flex-center">
+ ad banner
+ </span>
+ </div> */}
<EpisodeLists
info={info}
session={sessions}
@@ -556,3 +637,17 @@ export default function Watch({
</>
);
}
+
+function SpinLoader() {
+ return (
+ <div className="pointer-events-none absolute inset-0 z-50 flex h-full w-full items-center justify-center">
+ <Spinner.Root
+ className="text-white animate-spin opacity-100"
+ size={84}
+ >
+ <Spinner.Track className="opacity-25" width={8} />
+ <Spinner.TrackFill className="opacity-75" width={8} />
+ </Spinner.Root>
+ </div>
+ );
+}
diff --git a/pages/en/contact.js b/pages/en/contact.tsx
index 385bdb1..9954f95 100644
--- a/pages/en/contact.js
+++ b/pages/en/contact.tsx
@@ -1,10 +1,10 @@
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import Footer from "@/components/shared/footer";
const Contact = () => {
return (
<>
- <NewNavbar withNav={true} scrollP={5} shrink={true} />
+ <Navbar withNav={true} scrollP={5} shrink={true} />
<div className=" flex h-screen w-screen flex-col items-center justify-center font-karla font-bold">
<h1>Contact Us</h1>
<p>If you have any questions or comments, please email us at:</p>
diff --git a/pages/en/dmca.js b/pages/en/dmca.tsx
index e559829..eba28fe 100644
--- a/pages/en/dmca.js
+++ b/pages/en/dmca.tsx
@@ -1,5 +1,5 @@
import MobileNav from "@/components/shared/MobileNav";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import Footer from "@/components/shared/footer";
import Head from "next/head";
@@ -21,7 +21,7 @@ export default function DMCA() {
<link rel="icon" href="/svg/c.svg" />
</Head>
<>
- <NewNavbar withNav={true} scrollP={5} shrink={true} />
+ <Navbar withNav={true} scrollP={5} shrink={true} />
<MobileNav hideProfile={true} />
<div className="min-h-screen z-20 flex w-screen justify-center items-center">
diff --git a/pages/en/index.js b/pages/en/index.tsx
index 29b0778..4141015 100644
--- a/pages/en/index.js
+++ b/pages/en/index.tsx
@@ -14,11 +14,11 @@ import Schedule from "@/components/home/schedule";
import getUpcomingAnime from "@/lib/anilist/getUpcomingAnime";
import GetMedia from "@/lib/anilist/getMedia";
-// import UserRecommendation from "../../components/home/recommendation";
import MobileNav from "@/components/shared/MobileNav";
import { getGreetings } from "@/utils/getGreetings";
import { redis } from "@/lib/redis";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
+import UserRecommendation from "@/components/home/recommendation";
export async function getServerSideProps() {
let cachedData;
@@ -75,12 +75,55 @@ export async function getServerSideProps() {
}
}
-export default function Home({ detail, populars, upComing }) {
- const { data: sessions } = useSession();
- const { anime: currentAnime, manga: currentManga } = GetMedia(sessions, {
+type HomeProps = {
+ genre: any;
+ detail: any;
+ populars: any;
+ upComing: any;
+};
+
+export interface SessionTypes {
+ name: string;
+ picture: Picture;
+ sub: string;
+ token: string;
+ id: number;
+ image: Image;
+ list: string[];
+ version: string;
+ iat: number;
+ exp: number;
+ jti: string;
+}
+
+interface Picture {
+ large: string;
+ medium: string;
+}
+
+interface Image {
+ large: string;
+ medium: string;
+}
+
+export default function Home({ detail, populars, upComing }: HomeProps) {
+ const { data: sessions }: any = useSession();
+ const userSession: SessionTypes = sessions?.user;
+
+ const {
+ anime: currentAnime,
+ manga: currentManga,
+ recommendations,
+ }: {
+ anime: CurrentMediaTypes[];
+ manga: CurrentMediaTypes[];
+ recommendations: CurrentMediaTypes[];
+ } = GetMedia(sessions, {
stats: "CURRENT",
});
- const { anime: plan } = GetMedia(sessions, { stats: "PLANNING" });
+ const { anime: plan }: { anime: CurrentMediaTypes[] } = GetMedia(sessions, {
+ stats: "PLANNING",
+ });
const { anime: release } = GetMedia(sessions);
const [schedules, setSchedules] = useState(null);
@@ -97,12 +140,12 @@ export default function Home({ detail, populars, upComing }) {
}
useEffect(() => {
- if (sessions?.user?.version) {
- if (sessions.user.version !== "1.0.1") {
- signOut("AniListProvider");
+ if (userSession?.version) {
+ if (userSession?.version !== "1.0.1") {
+ signOut({ redirect: true });
}
}
- }, [sessions?.user?.version]);
+ }, [userSession?.version]);
useEffect(() => {
getRecent();
@@ -118,33 +161,15 @@ export default function Home({ detail, populars, upComing }) {
}
}, [upComing]);
- // useEffect(() => {
- // const getSchedule = async () => {
- // try {
- // const res = await fetch(`/api/v2/etc/schedule`);
- // const data = await res.json();
-
- // if (!res.ok) {
- // setSchedules(null);
- // } else {
- // setSchedules(data);
- // }
- // } catch (err) {
- // console.log(err);
- // }
- // };
- // getSchedule();
- // }, []);
-
- const [releaseData, setReleaseData] = useState([]);
+ const [releaseData, setReleaseData] = useState<any[]>([]);
useEffect(() => {
function getRelease() {
- let releasingAnime = [];
- let progress = [];
- let seenIds = new Set(); // Create a Set to store the IDs of seen anime
- release.map((list) => {
- list.entries.map((entry) => {
+ let releasingAnime: any[] = [];
+ let progress: any[] = [];
+ let seenIds = new Set<number>(); // Create a Set to store the IDs of seen anime
+ (release as any[]).forEach((list: any) => {
+ list.entries.forEach((entry: any) => {
if (
entry.media.status === "RELEASING" &&
!seenIds.has(entry.media.id)
@@ -156,18 +181,18 @@ export default function Home({ detail, populars, upComing }) {
});
});
setReleaseData(releasingAnime);
- setProg(progress);
+ if (progress.length > 0) setProg(progress);
}
getRelease();
}, [release]);
- const [listAnime, setListAnime] = useState(null);
- const [listManga, setListManga] = useState(null);
- const [planned, setPlanned] = useState(null);
- const [user, setUser] = useState(null);
+ const [listAnime, setListAnime] = useState<any[] | null>();
+ const [listManga, setListManga] = useState<any[] | null>(null);
+ const [planned, setPlanned] = useState<any[] | null>(null);
+ const [user, setUser] = useState<any[] | null>(null);
const [removed, setRemoved] = useState();
- const [prog, setProg] = useState(null);
+ const [prog, setProg] = useState<any[] | null>();
const popular = populars?.data;
const data = detail.data[0];
@@ -175,7 +200,7 @@ export default function Home({ detail, populars, upComing }) {
useEffect(() => {
async function userData() {
try {
- if (sessions?.user?.name) {
+ if (userSession?.name) {
await fetch(`/api/user/profile`, {
method: "POST",
headers: {
@@ -189,9 +214,9 @@ export default function Home({ detail, populars, upComing }) {
} catch (error) {
console.log(error);
}
- let data;
+ let data: UserDataType | null = null;
try {
- if (sessions?.user?.name) {
+ if (userSession?.name) {
const res = await fetch(
`/api/user/profile?name=${sessions.user.name}`
);
@@ -220,17 +245,20 @@ export default function Home({ detail, populars, upComing }) {
// Handle the error here
}
if (!data) {
- const dat = JSON.parse(localStorage.getItem("artplayer_settings"));
+ const dat: any = localStorage.getItem("artplayer_settings");
if (dat) {
- const arr = Object.keys(dat).map((key) => dat[key]);
- const newFirst = arr?.sort((a, b) => {
- return new Date(b?.createdAt) - new Date(a?.createdAt);
+ const arr = Object.keys(dat).map((key: string) => dat[key] as any);
+ const newFirst = arr?.sort((a: any, b: any) => {
+ return (
+ new Date(b?.createdAt).getTime() -
+ new Date(a?.createdAt).getTime()
+ );
});
const uniqueTitles = new Set();
// Filter out duplicates and store unique entries
- const filteredData = newFirst.filter((entry) => {
+ const filteredData = newFirst.filter((entry: any) => {
if (uniqueTitles.has(entry.aniTitle)) {
return false;
}
@@ -238,7 +266,9 @@ export default function Home({ detail, populars, upComing }) {
return true;
});
- setUser(filteredData);
+ if (filteredData) {
+ setUser(filteredData);
+ }
}
} else {
// Create a Set to store unique aniTitles
@@ -257,11 +287,11 @@ export default function Home({ detail, populars, upComing }) {
// const data = await res.json();
}
userData();
- }, [sessions?.user?.name, removed]);
+ }, [userSession?.name, removed]);
useEffect(() => {
async function userData() {
- if (!sessions?.user?.name) return;
+ if (!userSession?.name) return;
const getMedia =
currentAnime.find((item) => item.status === "CURRENT") || null;
@@ -292,9 +322,7 @@ export default function Home({ detail, populars, upComing }) {
userData();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [sessions?.user?.name, currentAnime, plan]);
-
- // console.log({ recentAdded });
+ }, [userSession?.name, currentAnime, plan]);
return (
<Fragment>
@@ -304,7 +332,6 @@ export default function Home({ detail, populars, upComing }) {
<link rel="icon" href="/svg/c.svg" />
<link rel="canonical" href="https://moopa.live/en/" />
<meta name="twitter:card" content="summary_large_image" />
- {/* Write the best SEO for this homepage */}
<meta
name="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!"
@@ -339,9 +366,9 @@ export default function Home({ detail, populars, upComing }) {
/>
<meta name="twitter:image" content="/preview.png" />
</Head>
- <MobileNav sessions={sessions} hideProfile={true} />
+ <MobileNav hideProfile={true} />
- <NewNavbar paddingY="pt-2 lg:pt-10" withNav={true} home={true} />
+ <Navbar paddingY="pt-2 lg:pt-10" withNav={true} home={true} />
<div className="h-auto w-screen bg-[#141519] text-[#dbdcdd]">
{/* PC / TABLET */}
<div className=" hidden justify-center lg:flex my-16">
@@ -381,6 +408,16 @@ export default function Home({ detail, populars, upComing }) {
</div>
</div>
</div>
+ {/* <div className="relative w-screen h-screen overflow-hidden">
+ <iframe
+ width="560"
+ height="315"
+ src="https://www.youtube.com/embed/VVfdqw-qvNE?autoplay=1&controls=0&rel=0&mute=1"
+ frameborder="0"
+ allowfullscreen
+ className="absolute w-screen h-screen top-0 scale-[115%] left-0 z-0"
+ />
+ </div> */}
{sessions && (
<div className="flex items-center justify-center lg:bg-none mt-4 lg:mt-0 w-screen">
@@ -411,7 +448,7 @@ export default function Home({ detail, populars, upComing }) {
animate={{ opacity: 1 }}
transition={{ duration: 0.5, staggerChildren: 0.2 }} // Add staggerChildren prop
>
- {user?.length > 0 && user?.some((i) => i?.watchId) && (
+ {user && user?.length > 0 && user?.some((i) => i?.watchId) && (
<motion.section // Add motion.div to each child component
key="recentlyWatched"
initial={{ y: 20, opacity: 0 }}
@@ -423,7 +460,7 @@ export default function Home({ detail, populars, upComing }) {
ids="recentlyWatched"
section="Recently Watched"
userData={user}
- userName={sessions?.user?.name}
+ userName={userSession?.name}
setRemoved={setRemoved}
/>
</motion.section>
@@ -442,12 +479,12 @@ export default function Home({ detail, populars, upComing }) {
section="On-Going Anime"
data={releaseData}
og={prog}
- userName={sessions?.user?.name}
+ userName={userSession?.name}
/>
</motion.section>
)}
- {sessions && listAnime?.length > 0 && (
+ {sessions && listAnime && listAnime?.length > 0 && (
<motion.section // Add motion.div to each child component
key="listAnime"
initial={{ y: 20, opacity: 0 }}
@@ -460,12 +497,12 @@ export default function Home({ detail, populars, upComing }) {
section="Your Watch List"
data={listAnime}
og={prog}
- userName={sessions?.user?.name}
+ userName={userSession?.name}
/>
</motion.section>
)}
- {sessions && listManga?.length > 0 && (
+ {sessions && listManga && listManga?.length > 0 && (
<motion.section // Add motion.div to each child component
key="listManga"
initial={{ y: 20, opacity: 0 }}
@@ -478,13 +515,13 @@ export default function Home({ detail, populars, upComing }) {
section="Your Manga List"
data={listManga}
// og={prog}
- userName={sessions?.user?.name}
+ userName={userSession?.name}
/>
</motion.section>
)}
- {/* {recommendations.length > 0 && (
- <div className="space-y-5 mb-10">
+ {recommendations.length > 0 && (
+ <div className="space-y-4 lg:space-y-5 mb-5 lg:mb-10">
<div className="px-5">
<p className="text-sm lg:text-base">
Based on Your List
@@ -496,10 +533,10 @@ export default function Home({ detail, populars, upComing }) {
</div>
<UserRecommendation data={recommendations} />
</div>
- )} */}
+ )}
{/* SECTION 2 */}
- {sessions && planned?.length > 0 && (
+ {sessions && planned && planned?.length > 0 && (
<motion.section // Add motion.div to each child component
key="plannedAnime"
initial={{ y: 20, opacity: 0 }}
@@ -511,7 +548,7 @@ export default function Home({ detail, populars, upComing }) {
ids="plannedAnime"
section="Your Plan"
data={planned}
- userName={sessions?.user?.name}
+ userName={userSession?.name}
/>
</motion.section>
)}
@@ -534,7 +571,7 @@ export default function Home({ detail, populars, upComing }) {
>
<Content
ids="recentAdded"
- section="New Episodes"
+ section="Freshly Added"
data={recentAdded}
/>
</motion.section>
@@ -556,6 +593,9 @@ export default function Home({ detail, populars, upComing }) {
/>
</motion.section>
)}
+ {/* <div className="w-full h-[150px] bg-white flex-center my-5 text-black">
+ ad banner
+ </div> */}
{/* Schedule */}
{anime.length > 0 && (
@@ -608,3 +648,65 @@ export default function Home({ detail, populars, upComing }) {
</Fragment>
);
}
+
+export interface CurrentMediaTypes {
+ status?: string;
+ name: string;
+ entries: Entry[];
+}
+
+export interface Entry {
+ id: number;
+ mediaId: number;
+ status: string;
+ progress: number;
+ score: number;
+ media: Media;
+}
+
+export interface Media {
+ id: number;
+ status: string;
+ nextAiringEpisode: any;
+ title: Title;
+ episodes: number;
+ coverImage: CoverImage;
+}
+
+export interface Title {
+ english: string;
+ romaji: string;
+}
+
+export interface CoverImage {
+ large: string;
+}
+
+export interface UserDataType {
+ id: string;
+ name: string;
+ setting: Setting;
+ WatchListEpisode: WatchListEpisode[];
+}
+
+export interface Setting {
+ CustomLists: boolean;
+}
+
+export interface WatchListEpisode {
+ id: string;
+ aniId?: string;
+ title?: string;
+ aniTitle?: string;
+ image?: string;
+ episode?: number;
+ timeWatched?: number;
+ duration?: number;
+ provider?: string;
+ nextId?: string;
+ nextNumber?: number;
+ dub?: boolean;
+ createdDate: string;
+ userProfileId: string;
+ watchId: string;
+}
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} />
diff --git a/pages/en/profile/[user].js b/pages/en/profile/[user].tsx
index 7ef5de3..82b88af 100644
--- a/pages/en/profile/[user].js
+++ b/pages/en/profile/[user].tsx
@@ -1,14 +1,28 @@
-import { getServerSession } from "next-auth";
-import { authOptions } from "../../api/auth/[...nextauth]";
import Image from "next/image";
import Link from "next/link";
import Head from "next/head";
import { useEffect, useState } from "react";
import { getUser } from "@/prisma/user";
-import { NewNavbar } from "@/components/shared/NavBar";
import { toast } from "sonner";
+import { Navbar } from "@/components/shared/NavBar";
+import pls from "@/utils/request";
+import { CurrentMediaTypes } from "..";
-export default function MyList({ media, sessions, user, time, userSettings }) {
+type MyListProps = {
+ media: CurrentMediaTypes[];
+ sessions: any;
+ user: any;
+ time: any;
+ userSettings: any;
+};
+
+export default function MyList({
+ media,
+ sessions,
+ user,
+ time,
+ userSettings,
+}: MyListProps) {
const [listFilter, setListFilter] = useState("all");
const [visible, setVisible] = useState(false);
const [useCustomList, setUseCustomList] = useState(true);
@@ -40,26 +54,27 @@ export default function MyList({ media, sessions, user, time, userSettings }) {
if (data) {
toast.success(`Custom List is now ${!useCustomList ? "on" : "off"}`);
}
- localStorage.setItem("customList", !useCustomList);
+ localStorage.setItem("customList", String(!useCustomList));
} catch (error) {
console.error(error);
}
};
- const filterMedia = (status) => {
+ const filterMedia = (status: string) => {
if (status === "all") {
return media;
}
- return media.filter((m) => m.name === status);
+ return media.filter((m: { name: string }) => m.name === status);
};
return (
<>
<Head>
<title>My Lists</title>
</Head>
- <NewNavbar />
- <div className="w-screen lg:flex justify-between lg:px-10 xl:px-32 py-5 relative">
+ <Navbar withNav toTop shrink bgHover scrollP={110} paddingY={"py-1"} />
+
+ <div className="w-screen lg:flex justify-between lg:px-10 xl:px-32 py-5 mt-10 xl:mt-16 relative">
<div className="lg:w-[30%] h-full mt-12 lg:mr-10 grid gap-5 mx-3 lg:mx-0 antialiased">
<div className="flex items-center gap-5">
<Image
@@ -289,7 +304,7 @@ export default function MyList({ media, sessions, user, time, userSettings }) {
<div className="absolute -top-10 -left-40 invisible lg:group-hover:visible">
<Image
src={item.media.coverImage.large}
- alt={item.media.id}
+ alt={String(item.media.id)}
width={1000}
height={1000}
className="object-cover h-[186px] w-[140px] shrink-0 rounded-md"
@@ -362,19 +377,14 @@ export default function MyList({ media, sessions, user, time, userSettings }) {
);
}
-export async function getServerSideProps(context) {
- const session = await getServerSession(context.req, context.res, authOptions);
- const accessToken = session?.user?.token || null;
+export async function getServerSideProps(context: any) {
const query = context.query;
- const response = await fetch("https://graphql.anilist.co/", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- ...(accessToken && { Authorization: `Bearer ${accessToken}` }),
- },
- body: JSON.stringify({
- query: `
+ const [data, session] = await pls.post(
+ "https://graphql.anilist.co/",
+ {
+ body: JSON.stringify({
+ query: `
query ($username: String, $status: MediaListStatus) {
MediaListCollection(userName: $username, type: ANIME, status: $status, sort: SCORE_DESC) {
user {
@@ -426,15 +436,15 @@ export async function getServerSideProps(context) {
}
}
`,
- variables: {
- username: query.user,
- },
- }),
- });
-
- const data = await response.json();
+ variables: {
+ username: query.user,
+ },
+ }),
+ },
+ context
+ );
- const get = data.data.MediaListCollection;
+ const get = data?.data?.MediaListCollection;
const sectionOrder = get?.user.mediaListOptions.animeList.sectionOrder;
if (!sectionOrder) {
@@ -451,12 +461,15 @@ export async function getServerSideProps(context) {
const prog = get.lists;
- function getIndex(status) {
+ function getIndex(status: string) {
const index = sectionOrder.indexOf(status);
return index === -1 ? sectionOrder.length : index;
}
- prog.sort((a, b) => getIndex(a.name) - getIndex(b.name));
+ prog.sort(
+ (a: { name: string }, b: { name: string }) =>
+ getIndex(a.name) - getIndex(b.name)
+ );
const user = get.user;
@@ -473,24 +486,24 @@ export async function getServerSideProps(context) {
};
}
-function UnixTimeConverter({ unixTime }) {
+function UnixTimeConverter({ unixTime }: { unixTime: number }) {
const date = new Date(unixTime * 1000); // multiply by 1000 to convert to milliseconds
const formattedDate = date.toISOString().slice(0, 10); // format date to YYYY-MM-DD
return <p>{formattedDate}</p>;
}
-function convertMinutesToDays(minutes) {
+function convertMinutesToDays(minutes: number) {
const hours = minutes / 60;
const days = hours / 24;
if (days >= 1) {
return days % 1 === 0
- ? { days: `${parseInt(days)}` }
+ ? { days: `${days}` }
: { days: `${days.toFixed(1)}` };
} else {
return hours % 1 === 0
- ? { hours: `${parseInt(hours)}` }
+ ? { hours: `${hours}` }
: { hours: `${hours.toFixed(1)}` };
}
}
diff --git a/pages/en/schedule/index.js b/pages/en/schedule/index.tsx
index f1e6730..aa30259 100644
--- a/pages/en/schedule/index.js
+++ b/pages/en/schedule/index.tsx
@@ -18,7 +18,7 @@ import MobileNav from "@/components/shared/MobileNav";
import { useSession } from "next-auth/react";
import { redis } from "@/lib/redis";
import Head from "next/head";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
const day = [
"Sunday",
@@ -30,7 +30,8 @@ const day = [
"Saturday",
];
-const isAired = (timestamp) => {
+const isAired = (timestamp: number | null) => {
+ if (!timestamp) return false;
const currentTime = new Date().getTime() / 1000;
return timestamp <= currentTime;
};
@@ -51,7 +52,7 @@ export async function getServerSideProps() {
0
);
const timeUntilMidnightJapan = Math.round(
- (midnightTomorrowJapan - nowJapan) / 1000
+ (midnightTomorrowJapan.getTime() - nowJapan.getTime()) / 1000
);
let cachedData;
@@ -109,12 +110,13 @@ export async function getServerSideProps() {
page++;
}
- const timestampToDay = (timestamp) => {
- const options = { weekday: "long" };
- return new Date(timestamp * 1000).toLocaleDateString(undefined, options);
+ const timestampToDay = (timestamp: number) => {
+ return new Date(timestamp * 1000).toLocaleDateString(undefined, {
+ weekday: "long",
+ });
};
- const scheduleByDay = {};
+ const scheduleByDay: { [key: string]: any } = {};
airingSchedules.forEach((schedule) => {
const day = timestampToDay(schedule.airingAt);
if (!scheduleByDay[day]) {
@@ -142,10 +144,7 @@ export async function getServerSideProps() {
// setSchedule(scheduleByDay);
}
-export default function Schedule({ schedule }) {
- const { data: session } = useSession();
-
- // const [schedule, setSchedule] = useState({});
+export default function Schedule({ schedule }: any) {
const [filterDay, setFilterDay] = useState("All");
const [loading, setLoading] = useState(true);
@@ -178,7 +177,7 @@ export default function Schedule({ schedule }) {
let nextAiring = null;
let currentlyAiring = null;
- for (const [, schedules] of Object.entries(sortedSchedule)) {
+ for (const [, schedules] of Object.entries(sortedSchedule as object)) {
for (const s of schedules) {
if (s.airingAt > now) {
if (!nextAiring) {
@@ -196,16 +195,16 @@ export default function Schedule({ schedule }) {
setCurrentlyAiringAnime(currentlyAiring);
}, [sortedSchedule]);
- const scrollContainerRef = useRef(null);
+ const scrollContainerRef = useRef<HTMLUListElement>(null);
useEffect(() => {
// Scroll to center the active button when it changes
if (scrollContainerRef.current) {
const activeButton =
- scrollContainerRef.current.querySelector(".text-action");
+ scrollContainerRef.current?.querySelector(".text-action");
if (activeButton) {
const containerWidth = scrollContainerRef.current.clientWidth;
- const buttonLeft = activeButton.offsetLeft;
+ const buttonLeft = (activeButton as HTMLElement).offsetLeft;
const buttonWidth = activeButton.clientWidth;
const scrollLeft = buttonLeft - containerWidth / 2 + buttonWidth / 2;
scrollContainerRef.current.scrollLeft = scrollLeft;
@@ -264,8 +263,8 @@ export default function Schedule({ schedule }) {
content="Moopa is a website where you can find all the information about your favorite anime and manga."
/>
</Head>
- <MobileNav sessions={session} hideProfile={true} />
- <NewNavbar scrollP={10} toTop={true} />
+ <MobileNav hideProfile={true} />
+ <Navbar scrollP={10} toTop={true} />
<div className="w-screen">
<span className="absolute z-20 top-0 left-0 w-screen h-[190px] lg:h-[250px] bg-secondary overflow-hidden">
<div className="absolute top-40 lg:top-36 w-full h-full bg-primary rounded-t-3xl xl:rounded-t-[50px]" />
@@ -340,7 +339,7 @@ export default function Schedule({ schedule }) {
>
<div className="ml-4 flex items-center gap-2">
<h3 className="text-lg text-gray-200 font-semibold">
- {timeStamptoAMPM(time)}
+ {time && timeStamptoAMPM(time)}
</h3>
{/* {!isAired(time) && <p>Airing Next</p>} */}
<p
diff --git a/pages/en/search/[...param].js b/pages/en/search/[...param].tsx
index c1fd94c..5a34ff5 100644
--- a/pages/en/search/[...param].js
+++ b/pages/en/search/[...param].tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef, useState } from "react";
+import { Key, useEffect, useRef, useState } from "react";
import { motion as m } from "framer-motion";
import Skeleton from "react-loading-skeleton";
import { useRouter } from "next/router";
@@ -23,12 +23,15 @@ import {
import InputSelect from "@/components/search/dropdown/inputSelect";
import { Cog6ToothIcon, TrashIcon } from "@heroicons/react/20/solid";
import useDebounce from "@/lib/hooks/useDebounce";
-import { NewNavbar } from "@/components/shared/NavBar";
+import { Navbar } from "@/components/shared/NavBar";
import MobileNav from "@/components/shared/MobileNav";
-import SearchByImage from "@/components/search/searchByImage";
+import SearchByImage, {
+ TraceMoeResultTypes,
+} from "@/components/search/searchByImage";
import { PlayIcon } from "@heroicons/react/24/outline";
+import { StaticImport } from "next/dist/shared/lib/get-img-props";
-export async function getServerSideProps(context) {
+export async function getServerSideProps(context: any) {
const { param } = context.query;
const { search, format, genres, season, year } = context.query;
@@ -81,6 +84,15 @@ export async function getServerSideProps(context) {
};
}
+type CardProps = {
+ index: number;
+ query: string;
+ genres: any;
+ formats: any;
+ seasons: any;
+ years: any;
+};
+
export default function Card({
index,
query,
@@ -88,22 +100,25 @@ export default function Card({
formats,
seasons,
years,
-}) {
+}: CardProps) {
const inputRef = useRef(null);
const router = useRouter();
- const [data, setData] = useState();
- const [imageSearch, setImageSearch] = useState();
+ const [data, setData] = useState<any>();
+ const [imageSearch, setImageSearch] = useState<TraceMoeResultTypes[]>();
const [loading, setLoading] = useState(true);
- const [search, setQuery] = useState(query);
+ const [search, setQuery] = useState<string | null | undefined>(query);
const debounceSearch = useDebounce(search, 500);
- const [type, setSelectedType] = useState(mediaType[index]);
+ const [type, setSelectedType] = useState<{
+ name: string;
+ value: string;
+ } | null>(mediaType[index]);
const [year, setYear] = useState(years);
const [season, setSeason] = useState(seasons);
- const [sort, setSelectedSort] = useState();
+ const [sort, setSelectedSort] = useState<{ name: string; value: string }>();
const [genre, setGenre] = useState(genres);
const [format, setFormat] = useState(formats);
@@ -116,7 +131,7 @@ export default function Card({
setLoading(true);
const data = await aniAdvanceSearch({
search: debounceSearch,
- type: type?.value,
+ type: type?.value as "ANIME" | "MANGA" | undefined,
genres: genre,
page: page,
sort: sort?.value,
@@ -128,7 +143,7 @@ export default function Card({
setNextPage(false);
setLoading(false);
} else if (data !== null && page > 1) {
- setData((prevData) => {
+ setData((prevData: any) => {
return [...(prevData ?? []), ...data?.media];
});
setNextPage(data?.pageInfo.hasNextPage);
@@ -144,7 +159,9 @@ export default function Card({
setData(null);
setPage(1);
setNextPage(true);
- advance();
+ if (page === 1) {
+ advance();
+ }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
debounceSearch,
@@ -158,7 +175,9 @@ export default function Card({
useEffect(() => {
if (imageSearch) return;
- advance();
+ if (page > 1) {
+ advance();
+ }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page, imageSearch]);
@@ -177,21 +196,23 @@ export default function Card({
window.innerHeight + window.pageYOffset >=
document.body.offsetHeight - 3
) {
- setPage((prevPage) => prevPage + 1);
+ if (!loading) {
+ setPage((prevPage) => prevPage + 1);
+ }
}
}
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
- }, [page, nextPage, imageSearch]);
+ }, [page, nextPage, imageSearch, loading]);
- const handleKeyDown = async (event) => {
+ const handleKeyDown = async (event: any) => {
if (event.key === "Enter") {
event.preventDefault();
const inputValue = event.target.value;
if (inputValue === "") {
- setQuery(null);
+ setQuery(undefined);
} else {
setQuery(inputValue);
}
@@ -199,13 +220,13 @@ export default function Card({
};
function trash() {
- setImageSearch();
- setQuery();
- setGenre();
- setFormat();
- setSelectedSort();
- setSeason();
- setYear();
+ setImageSearch(undefined);
+ setQuery(undefined);
+ setGenre(undefined);
+ setFormat(undefined);
+ setSelectedSort(undefined);
+ setSeason(undefined);
+ setYear(undefined);
router.push(`/en/search/${mediaType[index]?.value?.toLowerCase()}`);
}
@@ -213,8 +234,8 @@ export default function Card({
setIsVisible(!isVisible);
}
- const handleVideoHover = (hovered, id) => {
- const updatedImageSearch = imageSearch?.map((item) => {
+ const handleVideoHover = (hovered: boolean, id: any) => {
+ const updatedImageSearch = imageSearch?.map((item: any) => {
if (item.filename === id) {
return { ...item, hovered };
}
@@ -234,7 +255,7 @@ export default function Card({
<link rel="icon" href="/svg/c.svg" />
</Head>
- <NewNavbar
+ <Navbar
scrollP={10}
withNav={true}
shrink={true}
@@ -366,7 +387,7 @@ export default function Card({
</div>
)}
{/* <div> */}
- <div className="flex flex-col gap-14 items-center z-30">
+ <div className="flex flex-col gap-14 items-center z-30 overflow-x-hidden">
<div
key="card-keys"
className={`${
@@ -384,69 +405,75 @@ export default function Card({
{data &&
data?.length > 0 &&
!imageSearch &&
- data?.map((anime, index) => {
- const anilistId = anime?.mappings?.find(
- (x) => x.providerId === "anilist"
- )?.id;
- return (
- <m.div
- initial={{ scale: 0.98 }}
- animate={{ scale: 1, transition: { duration: 0.35 } }}
- className="w-full"
- key={index}
- >
- <Link
- href={
- anime.format === "MANGA" || anime.format === "NOVEL"
- ? `/en/manga/${anilistId ? `${anilistId}/` : ""}${
- anime.id
- }`
- : `/en/anime/${anime.id}`
- }
- title={anime.title.userPreferred}
- className="block relative overflow-hidden bg-secondary hover:scale-[1.03] scale-100 transition-all cursor-pointer duration-200 ease-out rounded"
- style={{
- paddingTop: "145%", // 2:3 aspect ratio (3/2 * 100%)
- }}
- >
- <Image
- className="object-cover"
- src={anime.coverImage.extraLarge}
- alt={anime.title.userPreferred}
- sizes="(min-width: 808px) 50vw, 100vw"
- quality={100}
- fill
- />
- </Link>
- <Link
- href={
- anime.format === "MANGA" || anime.format === "NOVEL"
- ? `/en/manga/${anilistId ? `${anilistId}/` : ""}${
- anime.id
- }`
- : `/en/anime/${anime.id}`
- }
- title={anime.title.userPreferred}
+ data?.map(
+ (
+ anime: {
+ format: string;
+ id: any;
+ title: { userPreferred: string };
+ coverImage: { extraLarge: string | StaticImport };
+ status: string;
+ episodes: any;
+ chapters: any;
+ },
+ index: Key | null | undefined
+ ) => {
+ return (
+ <m.div
+ initial={{ scale: 0.98 }}
+ animate={{ scale: 1, transition: { duration: 0.35 } }}
+ className="w-full"
+ key={index}
>
- <h1 className="font-outfit font-bold xl:text-base text-[15px] pt-4 line-clamp-2">
- {anime.status === "RELEASING" ? (
- <span className="dots bg-green-500" />
- ) : anime.status === "NOT_YET_RELEASED" ? (
- <span className="dots bg-red-500" />
- ) : null}
- {anime.title.userPreferred}
- </h1>
- </Link>
- <h2 className="font-outfit xl:text-[15px] text-[11px] font-light pt-2 text-[#8B8B8B]">
- {anime.format || <p>-</p>} &#183;{" "}
- {anime.status || <p>-</p>} &#183;{" "}
- {anime.episodes
- ? `${anime.episodes || "N/A"} Episodes`
- : `${anime.chapters || "N/A"} Chapters`}
- </h2>
- </m.div>
- );
- })}
+ <Link
+ href={
+ anime.format === "MANGA" || anime.format === "NOVEL"
+ ? `/en/manga/${anime.id}`
+ : `/en/anime/${anime.id}`
+ }
+ title={anime.title.userPreferred}
+ className="block relative overflow-hidden bg-secondary hover:scale-[1.03] scale-100 transition-all cursor-pointer duration-200 ease-out rounded"
+ style={{
+ paddingTop: "145%", // 2:3 aspect ratio (3/2 * 100%)
+ }}
+ >
+ <Image
+ className="object-cover"
+ src={anime.coverImage.extraLarge}
+ alt={anime.title.userPreferred}
+ sizes="(min-width: 808px) 50vw, 100vw"
+ quality={100}
+ fill
+ />
+ </Link>
+ <Link
+ href={
+ anime.format === "MANGA" || anime.format === "NOVEL"
+ ? `/en/manga/${anime.id}`
+ : `/en/anime/${anime.id}`
+ }
+ title={anime.title.userPreferred}
+ >
+ <h1 className="font-outfit font-bold xl:text-base text-[15px] pt-4 line-clamp-2">
+ {anime.status === "RELEASING" ? (
+ <span className="dots bg-green-500" />
+ ) : anime.status === "NOT_YET_RELEASED" ? (
+ <span className="dots bg-red-500" />
+ ) : null}
+ {anime.title.userPreferred}
+ </h1>
+ </Link>
+ <h2 className="font-outfit xl:text-[15px] text-[11px] font-light pt-2 text-[#8B8B8B]">
+ {anime.format || <p>-</p>} &#183;{" "}
+ {anime.status || <p>-</p>} &#183;{" "}
+ {anime.episodes
+ ? `${anime.episodes || "N/A"} Episodes`
+ : `${anime.chapters || "N/A"} Chapters`}
+ </h2>
+ </m.div>
+ );
+ }
+ )}
{loading && (
<>
@@ -532,7 +559,7 @@ export default function Card({
href={`/en/anime/${a.anilist.id}`}
>
{/* <h1 className="font-semibold">{a.title}</h1> */}
- <p className="flex items-center gap-1 text-sm text-gray-400 w-[320px]">
+ <p className="flex items-center gap-1 text-sm text-gray-400 max-w-[320px]">
<span
className="text-white max-w-[120px] md:max-w-[200px] lg:max-w-[220px]"
style={{