aboutsummaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-10-22 19:43:17 +0700
committerGitHub <[email protected]>2023-10-22 19:43:17 +0700
commitf801f8f422954b884a6541321dba0669ee9d6173 (patch)
treee0d5e106b99e9b4e0a4c4bf7bb0464617db85b8d /components
parentBump @babel/traverse from 7.22.8 to 7.23.2 (#90) (diff)
downloadmoopa-4.2.0.tar.xz
moopa-4.2.0.zip
Update v4.2.0 (#93)v4.2.0
Diffstat (limited to 'components')
-rw-r--r--components/admin/dashboard/index.js220
-rw-r--r--components/admin/meta/AppendMeta.js3
-rw-r--r--components/anime/episode.js34
-rw-r--r--components/anime/mobile/topSection.js68
-rw-r--r--components/anime/viewMode/listMode.js2
-rw-r--r--components/home/content.js17
-rw-r--r--components/home/genres.js1
-rw-r--r--components/home/schedule.js27
-rw-r--r--components/listEditor.js53
-rw-r--r--components/manga/chapters.js236
-rw-r--r--components/manga/info/mobile/mobileButton.js39
-rw-r--r--components/manga/info/mobile/topMobile.js16
-rw-r--r--components/manga/info/topSection.js107
-rw-r--r--components/manga/leftBar.js56
-rw-r--r--components/manga/mobile/bottomBar.js36
-rw-r--r--components/manga/mobile/hamburgerMenu.js228
-rw-r--r--components/manga/panels/firstPanel.js72
-rw-r--r--components/manga/panels/secondPanel.js61
-rw-r--r--components/manga/panels/thirdPanel.js46
-rw-r--r--components/manga/rightBar.js128
-rw-r--r--components/search/searchByImage.js119
-rw-r--r--components/searchPalette.js22
-rw-r--r--components/secret.js36
-rw-r--r--components/shared/NavBar.js7
-rw-r--r--components/shared/bugReport.js12
-rw-r--r--components/shared/footer.js2
-rw-r--r--components/watch/player/artplayer.js8
-rw-r--r--components/watch/player/component/controls/subtitle.js3
-rw-r--r--components/watch/player/playerComponent.js84
-rw-r--r--components/watch/player/utils/getZoroSource.js0
-rw-r--r--components/watch/secondary/episodeLists.js47
31 files changed, 975 insertions, 815 deletions
diff --git a/components/admin/dashboard/index.js b/components/admin/dashboard/index.js
index 64a1d6f..d0c9963 100644
--- a/components/admin/dashboard/index.js
+++ b/components/admin/dashboard/index.js
@@ -1,4 +1,6 @@
-import React, { useState } from "react";
+import Link from "next/link";
+import React, { useEffect, useState } from "react";
+import { toast } from "sonner";
export default function AdminDashboard({
animeCount,
@@ -10,13 +12,90 @@ export default function AdminDashboard({
const [selectedTime, setSelectedTime] = useState("");
const [unixTimestamp, setUnixTimestamp] = useState(null);
- const handleSubmit = (e) => {
+ const [broadcast, setBroadcast] = useState();
+ const [reportId, setReportId] = useState();
+
+ useEffect(() => {
+ async function getBroadcast() {
+ const res = await fetch("/api/v2/admin/broadcast", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Broadcast-Key": "get-broadcast",
+ },
+ });
+ const data = await res.json();
+ if (data) {
+ setBroadcast(data);
+ }
+ }
+ getBroadcast();
+ }, []);
+
+ const handleSubmit = async (e) => {
e.preventDefault();
+ let unixTime;
+
if (selectedTime) {
- const unixTime = Math.floor(new Date(selectedTime).getTime() / 1000);
+ unixTime = Math.floor(new Date(selectedTime).getTime() / 1000);
setUnixTimestamp(unixTime);
}
+
+ const res = await fetch("/api/v2/admin/broadcast", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Broadcast-Key": "get-broadcast",
+ },
+ body: JSON.stringify({
+ message,
+ startAt: unixTime,
+ show: true,
+ }),
+ });
+
+ const data = await res.json();
+
+ console.log({ message, unixTime, data });
+ };
+
+ const handleRemove = async () => {
+ try {
+ const res = await fetch("/api/v2/admin/broadcast", {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Broadcast-Key": "get-broadcast",
+ },
+ });
+ const data = await res.json();
+ console.log(data);
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ const handleResolved = async () => {
+ try {
+ console.log(reportId);
+ if (!reportId) return toast.error("reportId is required");
+ const res = await fetch("/api/v2/admin/bug-report", {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ reportId,
+ }),
+ });
+ const data = await res.json();
+ if (res.status === 200) {
+ toast.success(data.message);
+ }
+ } catch (error) {
+ console.log(`error while resolving ${error}`);
+ }
};
return (
<div className="flex flex-col gap-5 px-5 py-10 h-full">
@@ -39,7 +118,21 @@ export default function AdminDashboard({
</div>
<div className="grid grid-cols-2 gap-5 h-full">
<div className="flex flex-col gap-2">
- <p className="font-semibold">Broadcast</p>
+ <p className="flex items-center gap-2 font-semibold">
+ Broadcast
+ <span className="relative w-5 h-5 flex-center shrink-0">
+ <span
+ className={`absolute animate-ping inline-flex h-full w-full rounded-full ${
+ broadcast?.show === true ? "bg-green-500" : "hidden"
+ } opacity-75`}
+ ></span>
+ <span
+ className={`relative inline-flex rounded-full h-3 w-3 ${
+ broadcast?.show === true ? "bg-green-500" : "bg-red-500"
+ }`}
+ ></span>
+ </span>
+ </p>
<div className="flex flex-col justify-between bg-secondary rounded p-5 h-full">
<form onSubmit={handleSubmit}>
<div className="mb-4">
@@ -70,16 +163,24 @@ export default function AdminDashboard({
id="selectedTime"
value={selectedTime}
onChange={(e) => setSelectedTime(e.target.value)}
- required
className="w-full px-3 py-2 border rounded-md focus:outline-none text-black"
/>
</div>
- <button
- type="submit"
- className="bg-image text-white py-2 px-4 rounded-md hover:bg-opacity-80 transition duration-300"
- >
- Submit
- </button>
+ <div className="flex font-karla font-semibold gap-2">
+ <button
+ type="submit"
+ className="bg-image text-white py-2 px-4 rounded-md hover:bg-opacity-80 transition duration-300"
+ >
+ Broadcast
+ </button>
+ <button
+ type="button"
+ onClick={handleRemove}
+ className="bg-red-700 text-white py-2 px-4 rounded-md hover:bg-opacity-80 transition duration-300"
+ >
+ Remove
+ </button>
+ </div>
</form>
{unixTimestamp && (
<p>
@@ -95,40 +196,85 @@ export default function AdminDashboard({
{report?.map((i, index) => (
<div
key={index}
- className="odd:bg-primary/80 even:bg-primary/40 p-2 flex justify-between items-center"
+ className="odd:bg-primary/80 even:bg-primary/40 hover:odd:bg-image/20 hover:even:bg-image/20 p-2 flex justify-between items-center"
>
- {i.desc}{" "}
- {i.severity === "Low" && (
- <span className="relative w-5 h-5 flex-center shrink-0">
- {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
- <span className="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
- </span>
- )}
- {i.severity === "Medium" && (
- <span className="relative w-5 h-5 flex-center shrink-0">
- {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
- <span className="relative inline-flex rounded-full h-3 w-3 bg-amber-500"></span>
- </span>
- )}
- {i.severity === "High" && (
- <span className="relative w-5 h-5 flex-center shrink-0">
- {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
- <span className="relative animate-pulse inline-flex rounded-full h-3 w-3 bg-rose-500"></span>
- </span>
- )}
- {i.severity === "Critical" && (
- <span className="relative w-5 h-5 flex-center shrink-0">
- <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-900 opacity-75"></span>
- <span className="relative inline-flex rounded-full h-3 w-3 bg-red-900"></span>
+ <Link
+ href={i.url}
+ className="flex font-inter items-center gap-2 group"
+ >
+ {i.desc}{" "}
+ <span className="w-4 h-4 text-image group-hover:text-white">
+ <svg
+ fill="none"
+ stroke="currentColor"
+ strokeWidth={1.5}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ aria-hidden="true"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
+ />
+ </svg>
</span>
- )}
+ </Link>
+ <div className="flex items-center gap-2">
+ {i.severity === "Low" && (
+ <span className="relative w-5 h-5 flex-center shrink-0">
+ {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
+ <span className="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
+ </span>
+ )}
+ {i.severity === "Medium" && (
+ <span className="relative w-5 h-5 flex-center shrink-0">
+ {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
+ <span className="relative inline-flex rounded-full h-3 w-3 bg-amber-500"></span>
+ </span>
+ )}
+ {i.severity === "High" && (
+ <span className="relative w-5 h-5 flex-center shrink-0">
+ {/* <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-500 opacity-75"></span> */}
+ <span className="relative animate-pulse inline-flex rounded-full h-3 w-3 bg-rose-500"></span>
+ </span>
+ )}
+ {i.severity === "Critical" && (
+ <span className="relative w-5 h-5 flex-center shrink-0">
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-900 opacity-75"></span>
+ <span className="relative inline-flex rounded-full h-3 w-3 bg-red-900"></span>
+ </span>
+ )}
+ <button
+ type="button"
+ onClick={() => {
+ setReportId(i?.id);
+ handleResolved();
+ }}
+ className="w-6 h-6 hover:text-green-500"
+ >
+ <svg
+ fill="none"
+ stroke="currentColor"
+ strokeWidth={1.5}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ aria-hidden="true"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M4.5 12.75l6 6 9-13.5"
+ />
+ </svg>
+ </button>
+ </div>
</div>
))}
</div>
</div>
</div>
</div>
- <div className="w-full h-full">a</div>
</div>
);
}
diff --git a/components/admin/meta/AppendMeta.js b/components/admin/meta/AppendMeta.js
index 1707ed2..e49fcad 100644
--- a/components/admin/meta/AppendMeta.js
+++ b/components/admin/meta/AppendMeta.js
@@ -1,7 +1,7 @@
import Loading from "@/components/shared/loading";
import Image from "next/image";
import { useState } from "react";
-import { toast } from "react-toastify";
+import { toast } from "sonner";
// Define a function to convert the data
function convertData(episodes) {
@@ -217,6 +217,7 @@ export default function AppendMeta({ api }) {
</p>
<Image
src={i.image}
+ alt="query-image"
width={500}
height={500}
className="w-[160px] h-[210px] object-cover"
diff --git a/components/anime/episode.js b/components/anime/episode.js
index 25ed997..a42307f 100644
--- a/components/anime/episode.js
+++ b/components/anime/episode.js
@@ -4,7 +4,7 @@ import ViewSelector from "./viewSelector";
import ThumbnailOnly from "./viewMode/thumbnailOnly";
import ThumbnailDetail from "./viewMode/thumbnailDetail";
import ListMode from "./viewMode/listMode";
-import { toast } from "react-toastify";
+import { toast } from "sonner";
export default function AnimeEpisode({
info,
@@ -34,16 +34,12 @@ export default function AnimeEpisode({
info.status === "RELEASING" ? "true" : "false"
}${isDub ? "&dub=true" : ""}`
).then((res) => res.json());
- const getMap = response.find((i) => i?.map === true) || response[0];
+ const getMap = response.find((i) => i?.map === true);
let allProvider = response;
if (getMap) {
allProvider = response.filter((i) => {
- if (
- i?.providerId === "gogoanime" &&
- i?.providerId === "9anime" &&
- i?.map !== true
- ) {
+ if (i?.providerId === "gogoanime" && i?.map !== true) {
return null;
}
return i;
@@ -66,9 +62,12 @@ export default function AnimeEpisode({
fetchData();
return () => {
+ setCurrentPage(1);
setProviders(null);
setMapProviders(null);
};
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [info.id, isDub]);
const episodes =
@@ -79,9 +78,7 @@ export default function AnimeEpisode({
const lastEpisodeIndex = currentPage * itemsPerPage;
const firstEpisodeIndex = lastEpisodeIndex - itemsPerPage;
let currentEpisodes = episodes.slice(firstEpisodeIndex, lastEpisodeIndex);
- if (isDub) {
- currentEpisodes = currentEpisodes.filter((i) => i.hasDub === true);
- }
+
const totalPages = Math.ceil(episodes.length / itemsPerPage);
const handleChange = (event) => {
@@ -104,6 +101,7 @@ export default function AnimeEpisode({
) {
setView(3);
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [providerId, episodes]);
useEffect(() => {
@@ -122,6 +120,7 @@ export default function AnimeEpisode({
setWatch(null);
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [episodes]);
useEffect(() => {
@@ -157,6 +156,7 @@ export default function AnimeEpisode({
return;
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [providerId, artStorage, info.id, session?.user?.name]);
let debounceTimeout;
@@ -173,12 +173,7 @@ export default function AnimeEpisode({
);
if (!res.ok) {
console.log(res);
- toast.error("Something went wrong", {
- position: "bottom-left",
- autoClose: 3000,
- hideProgressBar: true,
- theme: "colored",
- });
+ toast.error("Something went wrong");
setProviders([]);
setLoading(false);
} else {
@@ -213,12 +208,7 @@ export default function AnimeEpisode({
}, 1000);
} catch (err) {
console.log(err);
- toast.error("Something went wrong", {
- position: "bottom-left",
- autoClose: 3000,
- hideProgressBar: true,
- theme: "colored",
- });
+ toast.error("Something went wrong");
}
};
diff --git a/components/anime/mobile/topSection.js b/components/anime/mobile/topSection.js
index 761a9fd..e5f58da 100644
--- a/components/anime/mobile/topSection.js
+++ b/components/anime/mobile/topSection.js
@@ -1,4 +1,9 @@
-import { PlayIcon, PlusIcon, ShareIcon } from "@heroicons/react/24/solid";
+import {
+ BookOpenIcon,
+ PlayIcon,
+ PlusIcon,
+ ShareIcon,
+} from "@heroicons/react/24/solid";
import Image from "next/image";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
@@ -21,6 +26,8 @@ export default function DetailTop({
const [showAll, setShowAll] = useState(false);
+ const isAnime = info.type === "ANIME";
+
useEffect(() => {
setReadMore(false);
}, [info.id]);
@@ -29,7 +36,7 @@ export default function DetailTop({
try {
if (navigator.share) {
await navigator.share({
- title: `Watch Now - ${info?.title?.english}`,
+ title: `${isAnime ? "Watch" : "Read"} Now - ${info?.title?.english}`,
// text: `Watch [${info?.title?.romaji}] and more on Moopa. Join us for endless anime entertainment"`,
url: window.location.href,
});
@@ -50,7 +57,7 @@ export default function DetailTop({
<div className="flex flex-col md:flex-row w-full items-center md:items-end gap-5 pt-12">
<div className="shrink-0 w-[180px] h-[250px] rounded overflow-hidden">
<Image
- src={info?.coverImage?.extraLarge}
+ src={info?.coverImage?.extraLarge || info?.coverImage}
alt="poster anime"
width={300}
height={300}
@@ -59,8 +66,9 @@ export default function DetailTop({
</div>
<div className="flex flex-col gap-4 items-center md:items-start justify-end w-full">
<div className="flex flex-col gap-1 text-center md:text-start">
- <h3 className="font-karla text-lg capitalize leading-none">
- {info?.season?.toLowerCase()} {info.seasonYear}
+ <h3 className="font-karla text-lg capitalize leading-none">
+ {info?.season?.toLowerCase() || getMonth(info?.startDate?.month)}{" "}
+ {info.seasonYear || info?.startDate?.year}
</h3>
<h1 className="font-outfit font-extrabold text-2xl md:text-4xl line-clamp-2 text-white">
{info?.title?.romaji || info?.title?.english}
@@ -87,12 +95,20 @@ export default function DetailTop({
onClick={() => router.push(watchUrl)}
className={`${
!watchUrl ? "opacity-30 pointer-events-none" : ""
- } w-[180px] flex-center text-lg font-karla font-semibold gap-1 border-black border-opacity-10 text-black rounded-full py-1 px-4 bg-white hover:opacity-80`}
+ } w-[180px] flex-center text-lg font-karla font-semibold gap-2 border-black border-opacity-10 text-black rounded-full py-1 px-4 bg-white hover:opacity-80`}
>
- <PlayIcon className="w-5 h-5" />
+ {isAnime ? (
+ <PlayIcon className="w-5 h-5" />
+ ) : (
+ <BookOpenIcon className="w-5 h-5" />
+ )}
{progress > 0 ? (
statuses?.value === "COMPLETED" ? (
- "Rewatch"
+ isAnime ? (
+ "Rewatch"
+ ) : (
+ "Reread"
+ )
) : !watchUrl && info?.nextAiringEpisode ? (
<span>
{convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
@@ -100,8 +116,10 @@ export default function DetailTop({
) : (
"Continue"
)
- ) : (
+ ) : isAnime ? (
"Watch Now"
+ ) : (
+ "Read Now"
)}
</button>
<div className="flex gap-2">
@@ -121,14 +139,14 @@ export default function DetailTop({
onClick={handleShareClick}
>
<span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
- Share Anime
+ Share {isAnime ? "Anime" : "Manga"}
</span>
<ShareIcon className="w-5 h-5" />
</button>
<a
target="_blank"
rel="noopener noreferrer"
- href={`https://anilist.co/anime/${info.id}`}
+ href={`https://anilist.co/${info.type.toLowerCase()}/${info.id}`}
className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
>
<span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
@@ -156,18 +174,24 @@ export default function DetailTop({
<PlusIcon className="w-5 h-5" />
</button>
<button
- // href={watchUrl || ""}
type="button"
- // disabled={!watchUrl || info?.nextAiringEpisode}
onClick={() => router.push(watchUrl)}
className={`${
!watchUrl ? "opacity-30 pointer-events-none" : ""
} flex items-center text-lg font-karla font-semibold gap-1 border-black border-opacity-10 text-black rounded-full py-2 px-4 bg-white`}
>
- <PlayIcon className="w-5 h-5" />
+ {isAnime ? (
+ <PlayIcon className="w-5 h-5" />
+ ) : (
+ <BookOpenIcon className="w-5 h-5" />
+ )}
{progress > 0 ? (
statuses?.value === "COMPLETED" ? (
- "Rewatch"
+ isAnime ? (
+ "Rewatch"
+ ) : (
+ "Reread"
+ )
) : !watchUrl && info?.nextAiringEpisode ? (
<span>
{convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
@@ -175,8 +199,10 @@ export default function DetailTop({
) : (
"Continue"
)
- ) : (
+ ) : isAnime ? (
"Watch Now"
+ ) : (
+ "Read Now"
)}
</button>
<button
@@ -185,7 +211,7 @@ export default function DetailTop({
onClick={handleShareClick}
>
<span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
- Share Anime
+ Share {isAnime ? "Anime" : "Manga"}
</span>
<ShareIcon className="w-5 h-5" />
</button>
@@ -287,3 +313,11 @@ export default function DetailTop({
</div>
);
}
+
+function getMonth(month) {
+ if (!month) return "";
+ const formattedMonth = new Date(0, month).toLocaleString("default", {
+ month: "long",
+ });
+ return formattedMonth;
+}
diff --git a/components/anime/viewMode/listMode.js b/components/anime/viewMode/listMode.js
index 5beded1..a6a1cf6 100644
--- a/components/anime/viewMode/listMode.js
+++ b/components/anime/viewMode/listMode.js
@@ -19,7 +19,7 @@ export default function ListMode({
href={`/en/anime/watch/${info.id}/${providerId}?id=${encodeURIComponent(
episode.id
)}&num=${episode.number}${dub ? `&dub=${dub}` : ""}`}
- className={`flex gap-3 py-4 hover:bg-secondary/10 odd:bg-secondary/30 even:bg-primary`}
+ className={`flex gap-3 py-4 hover:bg-secondary odd:bg-secondary/30 even:bg-primary`}
>
<div className="flex w-full">
<span className="shrink-0 px-4 text-center text-white/50">
diff --git a/components/home/content.js b/components/home/content.js
index 651d276..678549c 100644
--- a/components/home/content.js
+++ b/components/home/content.js
@@ -13,8 +13,8 @@ import { parseCookies } from "nookies";
import { ChevronLeftIcon } from "@heroicons/react/20/solid";
import { ExclamationCircleIcon, PlayIcon } from "@heroicons/react/24/solid";
import { useRouter } from "next/router";
-import { toast } from "react-toastify";
import HistoryOptions from "./content/historyOptions";
+import { toast } from "sonner";
export default function Content({
ids,
@@ -24,6 +24,7 @@ export default function Content({
og,
userName,
setRemoved,
+ type = "anime",
}) {
const router = useRouter();
@@ -53,6 +54,7 @@ export default function Content({
} else if (lang === "id") {
setLang("id");
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const [scrollLeft, setScrollLeft] = useState(false);
@@ -174,14 +176,7 @@ export default function Content({
setRemoved(id || aniId);
if (data?.message === "Episode deleted") {
- toast.success("Episode removed from history", {
- position: "bottom-right",
- autoClose: 5000,
- hideProgressBar: false,
- closeOnClick: true,
- draggable: true,
- theme: "dark",
- });
+ toast.success("Episode removed from history");
}
} else {
if (id) {
@@ -259,7 +254,7 @@ export default function Content({
href={
ids === "listManga"
? `/en/manga/${anime.id}`
- : `/${lang}/anime/${anime.id}`
+ : `/en/${type}/${anime.id}`
}
className="hover:scale-105 hover:shadow-lg duration-300 ease-out group relative"
title={anime.title.romaji}
@@ -352,7 +347,7 @@ export default function Content({
href={
ids === "listManga"
? `/en/manga/${anime.id}`
- : `/en/anime/${anime.id}`
+ : `/en/${type.toLowerCase()}/${anime.id}`
}
className="w-[135px] lg:w-[185px] line-clamp-2"
title={anime.title.romaji}
diff --git a/components/home/genres.js b/components/home/genres.js
index f054fc9..cd247ce 100644
--- a/components/home/genres.js
+++ b/components/home/genres.js
@@ -47,6 +47,7 @@ export default function Genres() {
} else if (lang === "id") {
setLang("id");
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="antialiased">
diff --git a/components/home/schedule.js b/components/home/schedule.js
index a0ab691..bb35d08 100644
--- a/components/home/schedule.js
+++ b/components/home/schedule.js
@@ -48,21 +48,26 @@ export default function Schedule({ data, scheduleData, anime, update }) {
<h1 className="font-bold font-karla text-[20px] lg:px-5">
Don't miss out!
</h1>
- <div className="rounded mb-5 shadow-md shadow-black">
+ <div className="rounded mb-5 shadow-tersier/50 shadow-button">
<div className="overflow-hidden w-full h-[96px] lg:h-[10rem] rounded relative">
- <div className="absolute flex flex-col lg:gap-1 justify-center pl-5 lg:pl-16 rounded z-20 bg-gradient-to-r from-30% from-tersier to-transparent w-full h-full">
- <h1 className="text-xs lg:text-lg">Coming Up Next!</h1>
- <div className="w-1/2 lg:w-2/5 hidden lg:block font-karla font-medium">
+ <div className="absolute flex flex-col -space-y-1 lg:gap-1 justify-center pl-5 lg:pl-16 rounded z-20 bg-gradient-to-r from-30% from-tersier to-transparent w-full h-full">
+ <h1 className="text-xs lg:text-lg font-karla font-thin">
+ Coming Up Next!
+ </h1>
+ <div className="w-1/2 lg:w-2/5 hidden lg:flex font-karla font-semibold line-clamp-2">
<Link
href={`/en/anime/${data.id}`}
- className="hover:underline underline-offset-4 decoration-2 leading-3 lg:text-[1.5vw]"
+ className="hover:underline underline-offset-4 decoration-2 leading-8 line-clamp-2 lg:text-[1.5vw]"
>
{data.title.romaji || data.title.english || data.title.native}
</Link>
</div>
- <h1 className="w-1/2 lg:hidden font-medium font-karla leading-9 text-white line-clamp-1">
+ <Link
+ href={`/en/anime/${data.id}`}
+ className="w-1/2 lg:hidden font-medium font-karla leading-9 text-white line-clamp-1"
+ >
{data.title.romaji || data.title.english || data.title.native}
- </h1>
+ </Link>
</div>
{data.bannerImage ? (
<Image
@@ -79,16 +84,16 @@ export default function Schedule({ data, scheduleData, anime, update }) {
height={500}
sizes="100vw"
alt="banner next anime"
- className="absolute z-10 top-0 right-0 h-full object-contain object-right brightness-[90%]"
+ className="absolute z-10 top-0 right-0 w-3/4 lg:w-auto h-full object-cover lg:object-contain object-right opacity-30 lg:opacity-100 brightness-[90%]"
/>
)}
<div
- className={`absolute flex justify-end items-center pr-5 gap-5 md:gap-10 z-20 w-1/2 h-full right-0 ${
- data.bannerImage ? "md:pr-16" : "md:pr-48"
+ className={`absolute flex justify-end items-center pr-5 gap-5 lg:gap-10 z-20 w-1/2 h-full right-0 ${
+ data.bannerImage ? "lg:pr-16" : "lg:pr-48"
}`}
>
{/* Countdown Timer */}
- <div className="flex items-center gap-2 md:gap-5 font-bold font-karla text-sm md:text-xl">
+ <div className="flex items-center gap-2 lg:gap-5 font-bold font-karla text-sm lg:text-xl">
{/* Countdown Timer */}
<div className="flex flex-col items-center">
<span className="text-action/80">{day}</span>
diff --git a/components/listEditor.js b/components/listEditor.js
index fa249e3..f4f46ea 100644
--- a/components/listEditor.js
+++ b/components/listEditor.js
@@ -1,7 +1,7 @@
import { useState } from "react";
import Image from "next/image";
-import { toast } from "react-toastify";
import { useRouter } from "next/router";
+import { toast } from "sonner";
const ListEditor = ({
animeId,
@@ -9,11 +9,12 @@ const ListEditor = ({
stats,
prg,
max,
- image = null,
+ info = null,
close,
}) => {
const [status, setStatus] = useState(stats ?? "CURRENT");
const [progress, setProgress] = useState(prg ?? 0);
+ const isAnime = info?.type === "ANIME";
const router = useRouter();
@@ -47,27 +48,11 @@ const ListEditor = ({
});
const { data } = await response.json();
if (data.SaveMediaListEntry === null) {
- toast.error("Something went wrong", {
- position: "bottom-right",
- autoClose: 5000,
- hideProgressBar: true,
- closeOnClick: false,
- pauseOnHover: true,
- draggable: true,
- theme: "colored",
- });
+ toast.error("Something went wrong");
return;
}
console.log("Saved media list entry", data);
- toast.success("Media list entry saved", {
- position: "bottom-right",
- autoClose: 5000,
- hideProgressBar: true,
- closeOnClick: false,
- pauseOnHover: true,
- draggable: true,
- theme: "dark",
- });
+ toast.success("Media list entry saved");
close();
setTimeout(() => {
// window.location.reload();
@@ -75,15 +60,7 @@ const ListEditor = ({
}, 1000);
// showAlert("Media list entry saved", "success");
} catch (error) {
- toast.error("Something went wrong", {
- position: "bottom-right",
- autoClose: 5000,
- hideProgressBar: true,
- closeOnClick: false,
- pauseOnHover: true,
- draggable: true,
- theme: "colored",
- });
+ toast.error("Something went wrong");
console.error(error);
}
};
@@ -95,10 +72,10 @@ const ListEditor = ({
</div>
<div className="relative bg-secondary rounded-sm w-screen md:w-auto">
<div className="md:flex">
- {image && (
+ {info?.bannerImage && (
<div>
<Image
- src={image.coverImage.large}
+ src={info.coverImage.large}
alt="image"
height={500}
width={500}
@@ -106,9 +83,9 @@ const ListEditor = ({
/>
<Image
src={
- image.bannerImage ||
- image.coverImage.extraLarge ||
- image.coverImage.large
+ info.bannerImage ||
+ info.coverImage.extraLarge ||
+ info.coverImage.large
}
alt="image"
height={500}
@@ -136,11 +113,15 @@ const ListEditor = ({
onChange={(e) => setStatus(e.target.value)}
className="rounded-sm px-2 py-1 bg-[#363642] w-[50%] sm:w-[150px] text-sm sm:text-base"
>
- <option value="CURRENT">Watching</option>
+ <option value="CURRENT">
+ {isAnime ? "Watching" : "Reading"}
+ </option>
<option value="COMPLETED">Completed</option>
<option value="PAUSED">Paused</option>
<option value="DROPPED">Dropped</option>
- <option value="PLANNING">Plan to watch</option>
+ <option value="PLANNING">
+ Plan to {isAnime ? "watch" : "read"}
+ </option>
</select>
</div>
<div className="flex justify-between items-center mt-2">
diff --git a/components/manga/chapters.js b/components/manga/chapters.js
index fd7beea..2150686 100644
--- a/components/manga/chapters.js
+++ b/components/manga/chapters.js
@@ -1,13 +1,16 @@
import Link from "next/link";
import { useState, useEffect } from "react";
-import { ChevronDownIcon } from "@heroicons/react/24/outline";
-import { setCookie } from "nookies";
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from "@heroicons/react/24/outline";
-const ChapterSelector = ({ chaptersData, data, setFirstEp, userManga }) => {
+const ChapterSelector = ({ chaptersData, data, setWatch, mangaId }) => {
const [selectedProvider, setSelectedProvider] = useState(
chaptersData[0]?.providerId || ""
);
- const [selectedChapter, setSelectedChapter] = useState("");
+ // const [selectedChapter, setSelectedChapter] = useState("");
const [chapters, setChapters] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [chaptersPerPage] = useState(10);
@@ -16,13 +19,15 @@ const ChapterSelector = ({ chaptersData, data, setFirstEp, userManga }) => {
const selectedChapters = chaptersData.find(
(c) => c.providerId === selectedProvider
);
- if (selectedChapters) {
- setSelectedChapter(selectedChapters);
- setFirstEp(selectedChapters);
- }
setChapters(selectedChapters?.chapters || []);
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedProvider, chaptersData]);
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [data.id]);
+
// Get current posts
const indexOfLastChapter = currentPage * chaptersPerPage;
const indexOfFirstChapter = indexOfLastChapter - chaptersPerPage;
@@ -31,24 +36,6 @@ const ChapterSelector = ({ chaptersData, data, setFirstEp, userManga }) => {
indexOfLastChapter
);
- // Change page
- const paginate = (pageNumber) => setCurrentPage(pageNumber);
- const nextPage = () => setCurrentPage((prev) => prev + 1);
- const prevPage = () => setCurrentPage((prev) => prev - 1);
-
- function saveManga() {
- localStorage.setItem(
- "manga",
- JSON.stringify({ manga: selectedChapter, data: data })
- );
- setCookie(null, "manga", data.id, {
- maxAge: 24 * 60 * 60,
- path: "/",
- });
- }
-
- // console.log(selectedChapter);
-
// Create page numbers
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(chapters.length / chaptersPerPage); i++) {
@@ -59,7 +46,7 @@ const ChapterSelector = ({ chaptersData, data, setFirstEp, userManga }) => {
const getDisplayedPageNumbers = (currentPage, totalPages, margin) => {
const pageRange = [...Array(totalPages).keys()].map((i) => i + 1);
- if (totalPages <= 10) {
+ if (totalPages <= 5) {
return pageRange;
}
@@ -83,104 +70,147 @@ const ChapterSelector = ({ chaptersData, data, setFirstEp, userManga }) => {
const displayedPageNumbers = getDisplayedPageNumbers(
currentPage,
pageNumbers.length,
- 9
+ 3
);
- // console.log(currentChapters);
+ useEffect(() => {
+ if (chapters) {
+ const getEpi = data?.nextAiringEpisode
+ ? chapters[data?.mediaListEntry?.progress]
+ : chapters[0];
+ if (getEpi) {
+ const watchUrl = `/en/manga/read/${selectedProvider}?id=${mangaId}&chapterId=${encodeURIComponent(
+ getEpi.id
+ )}&anilist=${data.id}&num=${getEpi.number}`;
+ setWatch(watchUrl);
+ } else {
+ setWatch(null);
+ }
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [chapters]);
return (
- <div className="flex flex-col items-center z-40">
- <div className="flex flex-col w-full">
- <label htmlFor="provider" className="text-sm md:text-base font-medium">
- Select a Provider
- </label>
- <div className="relative w-full">
+ <div className="flex flex-col gap-2 px-3">
+ <div className="flex justify-between">
+ <h1 className="text-[20px] lg:text-2xl font-bold font-karla">
+ Chapters
+ </h1>
+ <div className="relative flex gap-2 items-center group">
<select
id="provider"
- className="w-full text-xs md:text-base cursor-pointer mt-2 p-2 focus:outline-none rounded-md appearance-none bg-secondary"
+ className="flex items-center text-sm gap-5 rounded-[3px] bg-secondary py-1 px-3 pr-8 font-karla appearance-none cursor-pointer outline-none focus: focus:ring-action group-hover: group-hover:ring-action"
value={selectedProvider}
onChange={(e) => setSelectedProvider(e.target.value)}
>
{/* <option value="">--Select a provider--</option> */}
{chaptersData.map((provider, index) => (
- <option key={index} value={provider.providerId}>
+ <option key={provider.providerId} value={provider.providerId}>
{provider.providerId}
</option>
))}
</select>
- <ChevronDownIcon className="absolute md:right-5 right-3 md:bottom-2 m-auto md:w-6 md:h-6 bottom-[0.5rem] h-4 w-4" />
+ <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" />
</div>
</div>
- <div className="mt-4 w-full py-5 flex justify-between gap-5">
- <button
- onClick={prevPage}
- disabled={currentPage === 1}
- className={`w-24 py-1 shrink-0 rounded-md font-karla ${
- currentPage === 1
- ? "bg-[#1D1D20] text-[#313135]"
- : `bg-secondary hover:bg-[#363639]`
- }`}
- >
- Previous
- </button>
- <div className="flex gap-5 overflow-x-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb- w-[420px] lg:w-auto">
- {displayedPageNumbers.map((number, index) =>
- number === "..." ? (
- <span key={index + 2} className="w-10 py-1 text-center">
- ...
- </span>
- ) : (
+
+ <div className="flex flex-col items-center z-40">
+ <div className="mt-4 w-full">
+ {currentChapters.map((chapter, index) => {
+ const isRead = chapter.number <= data?.mediaListEntry?.progress;
+ return (
+ <Link
+ key={index}
+ href={`/en/manga/read/${selectedProvider}?id=${mangaId}&chapterId=${encodeURIComponent(
+ chapter.id
+ )}${data?.id?.length > 6 ? "" : `&anilist=${data.id}`}&num=${
+ chapter.number
+ }`}
+ className={`flex gap-3 py-4 hover:bg-secondary odd:bg-secondary/30 even:bg-primary`}
+ >
+ <div className="flex w-full">
+ <span className="shrink-0 px-4 text-center text-white/50">
+ {chapter.number}
+ </span>
+ <p
+ className={`w-full line-clamp-1 ${
+ isRead ? "text-[#5f5f5f]" : "text-white"
+ }
+ `}
+ >
+ {chapter.title || `Chapter ${chapter.number}`}
+ </p>
+ <p className="capitalize text-sm text-white/50 px-4">
+ {selectedProvider}
+ </p>
+ </div>
+ </Link>
+ );
+ })}
+ </div>
+
+ <div className="flex flex-col mt-5 md:flex-row w-full sm:items-center sm:justify-between">
+ <div className="flex-center">
+ <p className="text-sm text-txt">
+ Showing{" "}
+ <span className="font-medium">{indexOfFirstChapter + 1}</span> to{" "}
+ <span className="font-medium">
+ {indexOfLastChapter > chapters.length
+ ? chapters.length
+ : indexOfLastChapter}
+ </span>{" "}
+ of <span className="font-medium">{chapters.length}</span> chapters
+ </p>
+ </div>
+ <div className="flex-center">
+ <nav
+ className="isolate inline-flex space-x-1 rounded-md shadow-sm"
+ aria-label="Pagination"
+ >
<button
- key={number}
- onClick={() => paginate(number)}
- className={`w-10 shrink-0 py-1 rounded-md hover:bg-[#363639] ${
- number === currentPage ? "bg-[#363639]" : "bg-secondary"
+ onClick={() => setCurrentPage((prev) => prev - 1)}
+ disabled={currentPage === 1}
+ className={`relative inline-flex items-center rounded px-2 py-2 text-gray-400 hover:bg-secondary focus:z-20 focus:outline-offset-0 ${
+ currentPage === 1
+ ? "opacity-50 cursor-default pointer-events-none"
+ : ""
}`}
>
- {number}
+ <span className="sr-only">Previous</span>
+ <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" />
</button>
- )
- )}
- </div>
- <button
- onClick={nextPage}
- disabled={currentPage === pageNumbers.length}
- className={`w-24 py-1 shrink-0 rounded-md font-karla ${
- currentPage === pageNumbers.length
- ? "bg-[#1D1D20] text-[#313135]"
- : `bg-secondary hover:bg-[#363639]`
- }`}
- >
- Next
- </button>
- </div>
- <div className="mt-4 w-full">
- {currentChapters.map((chapter, index) => {
- const isRead = chapter.number <= userManga?.progress;
- return (
- <div key={index} className="p-2 border-b hover:bg-[#232325]">
- <Link
- href={`/en/manga/read/${selectedProvider}?id=${
- data.id
- }&chapterId=${encodeURIComponent(chapter.id)}`}
- onClick={saveManga}
+ <div className="flex w-full gap-1 overflow-x-scroll scrollbar-thin scrollbar-thumb-image scrollbar-thumb-rounded">
+ {displayedPageNumbers.map((pageNumber, index) => (
+ <button
+ key={index}
+ onClick={() => setCurrentPage(pageNumber)}
+ disabled={pageNumber === "..."}
+ className={`relative rounded inline-flex items-center px-4 py-2 text-sm font-semibold text-txt hover:bg-secondary focus:z-20 focus:outline-offset-0 ${
+ currentPage === pageNumber
+ ? "z-10 bg-secondary rounded text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-none"
+ : ""
+ }`}
+ >
+ {pageNumber}
+ </button>
+ ))}
+ </div>
+ <button
+ onClick={() => setCurrentPage((prev) => prev + 1)}
+ disabled={currentPage === pageNumbers.length}
+ className={`relative inline-flex items-center rounded px-2 py-2 text-gray-400 hover:bg-secondary focus:z-20 focus:outline-offset-0 ${
+ currentPage === pageNumbers.length
+ ? "opacity-50 cursor-default"
+ : ""
+ }`}
>
- <h2
- className={`text-lg font-medium ${
- isRead ? "text-[#424245]" : ""
- }`}
- >
- {chapter.title}
- </h2>
- <p
- className={`text-[#59595d] ${isRead ? "text-[#313133]" : ""}`}
- >
- Updated At: {new Date(chapter.updatedAt).toLocaleString()}
- </p>
- </Link>
- </div>
- );
- })}
+ <span className="sr-only">Next</span>
+ <ChevronRightIcon className="h-5 w-5" aria-hidden="true" />
+ </button>
+ </nav>
+ </div>
+ </div>
</div>
</div>
);
diff --git a/components/manga/info/mobile/mobileButton.js b/components/manga/info/mobile/mobileButton.js
deleted file mode 100644
index 0016b59..0000000
--- a/components/manga/info/mobile/mobileButton.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import Link from "next/link";
-import AniList from "../../../media/aniList";
-import { BookOpenIcon } from "@heroicons/react/24/outline";
-
-export default function MobileButton({ info, firstEp, saveManga }) {
- return (
- <div className="md:hidden flex items-center gap-4 w-full pb-3">
- <button
- disabled={!firstEp}
- onClick={saveManga}
- className={`${
- !firstEp
- ? "pointer-events-none text-white/50 bg-secondary/50"
- : "bg-secondary text-white"
- } lg:w-full font-bold shadow-md shadow-secondary hover:bg-secondary/90 hover:text-white/50 rounded`}
- >
- <Link
- href={`/en/manga/read/${firstEp?.providerId}?id=${
- info.id
- }&chapterId=${encodeURIComponent(
- firstEp?.chapters[firstEp.chapters.length - 1].id
- )}`}
- className="flex items-center text-xs font-karla gap-2 h-[30px] px-2"
- >
- <h1>Read Now</h1>
- <BookOpenIcon className="w-4 h-4" />
- </Link>
- </button>
- <Link
- href={`https://anilist.co/manga/${info.id}`}
- className="flex-center rounded bg-secondary shadow-md shadow-secondary h-[30px] lg:px-4 px-2"
- >
- <div className="flex-center w-5 h-5">
- <AniList />
- </div>
- </Link>
- </div>
- );
-}
diff --git a/components/manga/info/mobile/topMobile.js b/components/manga/info/mobile/topMobile.js
deleted file mode 100644
index 2e6b23a..0000000
--- a/components/manga/info/mobile/topMobile.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import Image from "next/image";
-
-export default function TopMobile({ info }) {
- return (
- <div className="md:hidden">
- <Image
- src={info.coverImage}
- width={500}
- height={500}
- alt="cover image"
- className="md:hidden absolute top-0 left-0 -translate-y-24 w-full h-[30rem] object-cover rounded-sm shadow-lg brightness-75"
- />
- <div className="absolute top-0 left-0 w-full -translate-y-24 h-[32rem] bg-gradient-to-t from-primary to-transparent from-50%"></div>
- </div>
- );
-}
diff --git a/components/manga/info/topSection.js b/components/manga/info/topSection.js
deleted file mode 100644
index 45d5f11..0000000
--- a/components/manga/info/topSection.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import Image from "next/image";
-import { BookOpenIcon } from "@heroicons/react/24/outline";
-import AniList from "../../media/aniList";
-import Link from "next/link";
-import TopMobile from "./mobile/topMobile";
-import MobileButton from "./mobile/mobileButton";
-
-export default function TopSection({ info, firstEp, setCookie }) {
- const slicedGenre = info.genres?.slice(0, 3);
-
- function saveManga() {
- localStorage.setItem(
- "manga",
- JSON.stringify({ manga: firstEp, data: info })
- );
-
- setCookie(null, "manga", info.id, {
- maxAge: 24 * 60 * 60,
- path: "/",
- });
- }
-
- return (
- <div className="flex md:gap-5 w-[90%] xl:w-[70%] z-30">
- <TopMobile info={info} />
- <div className="hidden md:block w-[7rem] xs:w-[10rem] lg:w-[15rem] space-y-3 shrink-0 rounded-sm">
- <Image
- src={info.coverImage}
- width={500}
- height={500}
- priority
- alt="cover image"
- className="hidden md:block object-cover h-[10rem] xs:h-[14rem] lg:h-[22rem] rounded-sm shadow-lg shadow-[#1b1b1f] bg-[#34343b]/20"
- />
-
- <div className="hidden md:flex items-center justify-between w-full lg:gap-5 pb-3">
- <button
- disabled={!firstEp}
- onClick={saveManga}
- className={`${
- !firstEp
- ? "pointer-events-none text-white/50 bg-tersier/50"
- : "bg-tersier text-white"
- } lg:w-full font-bold shadow-md shadow-[#0E0E0F] hover:bg-tersier/90 hover:text-white/50 rounded-md`}
- >
- <Link
- href={`/en/manga/read/${firstEp?.providerId}?id=${
- info.id
- }&chapterId=${encodeURIComponent(
- firstEp?.chapters[firstEp.chapters.length - 1].id
- )}`}
- className="flex items-center lg:justify-center text-sm lg:text-base font-karla gap-2 h-[35px] lg:h-[40px] px-2"
- >
- <h1>Read Now</h1>
- <BookOpenIcon className="w-5 h-5" />
- </Link>
- </button>
- <Link
- href={`https://anilist.co/manga/${info.id}`}
- className="flex-center rounded-md bg-tersier shadow-md shadow-[#0E0E0F] h-[35px] lg:h-[40px] lg:px-4 px-2"
- >
- <div className="flex-center w-5 h-5">
- <AniList />
- </div>
- </Link>
- </div>
- </div>
- <div className="w-full flex flex-col justify-start z-40">
- <div className="md:h-1/2 py-2 md:py-5 flex flex-col md:gap-2 justify-end">
- <h1 className="title text-xl md:text-2xl xl:text-3xl text-white font-semibold font-karla line-clamp-1 text-start">
- {info.title?.romaji || info.title?.english || info.title?.native}
- </h1>
- <span className="flex flex-wrap text-xs lg:text-sm md:text-[#747478]">
- {slicedGenre &&
- slicedGenre.map((genre, index) => {
- return (
- <div key={index} className="flex">
- {genre}
- {index < slicedGenre?.length - 1 && (
- <span className="mx-2 text-sm text-[#747478]">•</span>
- )}
- </div>
- );
- })}
- </span>
- </div>
-
- <MobileButton info={info} firstEp={firstEp} saveManga={saveManga} />
-
- <div className="hidden md:block relative h-1/2">
- {/* <span className="font-semibold text-sm">Description</span> */}
- <div
- className={`relative group h-[8rem] lg:h-[12.5rem] text-sm lg:text-base overflow-y-scroll scrollbar-hide`}
- >
- <p
- dangerouslySetInnerHTML={{ __html: info.description }}
- className="pb-5 pt-2 leading-5"
- />
- </div>
- <div
- className={`absolute bottom-0 w-full bg-gradient-to-b from-transparent to-secondary to-50% h-[2rem]`}
- />
- </div>
- </div>
- </div>
- );
-}
diff --git a/components/manga/leftBar.js b/components/manga/leftBar.js
index 17acd55..5a98115 100644
--- a/components/manga/leftBar.js
+++ b/components/manga/leftBar.js
@@ -1,14 +1,23 @@
+import { getHeaders, getRandomId } from "@/utils/imageUtils";
import { ArrowLeftIcon } from "@heroicons/react/24/solid";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
-export function LeftBar({ data, page, info, currentId, setSeekPage }) {
+export function LeftBar({
+ data,
+ page,
+ info,
+ currentId,
+ setSeekPage,
+ number,
+ mediaId,
+ providerId,
+}) {
const router = useRouter();
function goBack() {
router.push(`/en/manga/${info.id}`);
}
- // console.log(info);
return (
<div className="hidden lg:block shrink-0 w-[16rem] h-screen overflow-y-auto scrollbar-none bg-secondary relative group">
<div className="grid">
@@ -37,23 +46,27 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {
<h1 className="font-bold xl:text-lg">Chapters</h1>
<div className="px-2">
<div className="w-full text-sm xl:text-base px-1 h-[8rem] xl:h-[30vh] bg-[#161617] rounded-md overflow-auto scrollbar-thin scrollbar-thumb-[#363639] scrollbar-thumb-rounded-md hover:scrollbar-thumb-[#424245]">
- {data?.chapters?.map((x) => {
+ {data?.chapters?.map((x, index) => {
return (
<div
- key={x.id}
+ key={getRandomId()}
className={`${
x.id === currentId && "text-action"
} py-1 px-2 hover:bg-[#424245] rounded-sm`}
>
<Link
- href={`/en/manga/read/${data.providerId}?id=${
- info.id
- }&chapterId=${encodeURIComponent(x.id)}`}
+ href={`/en/manga/read/${
+ data.providerId
+ }?id=${mediaId}&chapterId=${encodeURIComponent(x.id)}${
+ info?.id?.length > 6 ? "" : `&anilist=${info?.id}`
+ }&num=${x.number}`}
className=""
>
<h1 className="line-clamp-1">
- <span className="font-bold">{x.number}.</span>{" "}
- {x.title}
+ <span className="font-bold">
+ {x.number || index + 1}.
+ </span>{" "}
+ {x.title || `Chapter ${x.number || index + 1}`}
</h1>
</Link>
</div>
@@ -69,28 +82,37 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {
<div className="text-center w-full px-1 h-[30vh] bg-[#161617] rounded-md overflow-auto scrollbar-thin scrollbar-thumb-[#363639] scrollbar-thumb-rounded-md hover:scrollbar-thumb-[#424245]">
{Array.isArray(page) ? (
<div className="grid grid-cols-2 gap-5 py-4 px-2 place-items-center">
- {page?.map((x) => {
+ {page?.map((x, index) => {
return (
<div
- key={x.url}
+ key={getRandomId()}
className="hover:bg-[#424245] cursor-pointer rounded-sm w-full"
>
<div
className="flex flex-col items-center cursor-pointer"
- onClick={() => setSeekPage(x.index)}
+ onClick={() => setSeekPage(index)}
>
<Image
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
x.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({ Referer: x.headers.Referer })
- )}`}
+ )}${
+ x?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(x?.headers)
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(providerId))
+ )}`
+ }`}
+ // &headers=${encodeURIComponent(
+ // JSON.stringify({ Referer: x.headers.Referer })
+ // )}
alt="chapter image"
width={100}
height={200}
className="w-full h-[120px] object-contain scale-90"
/>
- <h1>Page {x.index + 1}</h1>
+ <h1>Page {index + 1}</h1>
</div>
</div>
);
@@ -98,7 +120,7 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {
</div>
) : (
<div className="py-4">
- <p>{page.error || "No Pages."}</p>
+ <p>{page?.error || "No Pages."}</p>
</div>
)}
</div>
diff --git a/components/manga/mobile/bottomBar.js b/components/manga/mobile/bottomBar.js
index 6493dca..5b28de4 100644
--- a/components/manga/mobile/bottomBar.js
+++ b/components/manga/mobile/bottomBar.js
@@ -1,3 +1,4 @@
+import { getHeaders } from "@/utils/imageUtils";
import {
ChevronLeftIcon,
ChevronRightIcon,
@@ -14,12 +15,15 @@ export default function BottomBar({
nextChapter,
currentPage,
chapter,
- page,
+ data,
setSeekPage,
setIsOpen,
+ number,
+ mangadexId,
}) {
const [openPage, setOpenPage] = useState(false);
const router = useRouter();
+
return (
<div
className={`fixed lg:hidden flex flex-col gap-3 z-50 h-auto w-screen ${
@@ -39,7 +43,9 @@ export default function BottomBar({
router.push(
`/en/manga/read/${
chapter.providerId
- }?id=${id}&chapterId=${encodeURIComponent(prevChapter)}`
+ }?id=${mangadexId}&chapterId=${encodeURIComponent(
+ prevChapter.id
+ )}${id > 6 ? "" : `&anilist=${id}`}&num=${prevChapter.number}`
)
}
>
@@ -56,7 +62,9 @@ export default function BottomBar({
router.push(
`/en/manga/read/${
chapter.providerId
- }?id=${id}&chapterId=${encodeURIComponent(nextChapter)}`
+ }?id=${mangadexId}&chapterId=${encodeURIComponent(
+ nextChapter.id
+ )}${id > 6 ? "" : `&anilist=${id}`}&num=${nextChapter.number}`
)
}
>
@@ -82,13 +90,14 @@ export default function BottomBar({
<RectangleStackIcon className="w-5 h-5" />
</button>
</div>
- <span className="flex bg-secondary shadow-lg ring-1 ring-black ring-opacity-5 p-2 rounded-md">{`${currentPage}/${page.length}`}</span>
+ <span className="flex bg-secondary shadow-lg ring-1 ring-black ring-opacity-5 p-2 rounded-md">{`${currentPage}/${data?.length}`}</span>
</div>
{openPage && (
<div className="bg-secondary flex justify-center h-full w-screen py-2">
<div className="flex overflow-scroll">
- {Array.isArray(page) ? (
- page.map((x) => {
+ {Array.isArray(data) ? (
+ data.map((x, index) => {
+ const indx = index + 1;
return (
<div
key={x.url}
@@ -101,9 +110,18 @@ export default function BottomBar({
<Image
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
x.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({ Referer: x.headers.Referer })
- )}`}
+ )}${
+ x?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(x?.headers)
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(chapter.providerId))
+ )}`
+ }`}
+ // &headers=${encodeURIComponent(
+ // JSON.stringify({ Referer: x.headers.Referer })
+ // )}
alt="chapter image"
width={100}
height={200}
diff --git a/components/manga/mobile/hamburgerMenu.js b/components/manga/mobile/hamburgerMenu.js
deleted file mode 100644
index fcdbcce..0000000
--- a/components/manga/mobile/hamburgerMenu.js
+++ /dev/null
@@ -1,228 +0,0 @@
-import React, { useState, useEffect } from "react";
-import Link from "next/link";
-import { useSession, signIn, signOut } from "next-auth/react";
-import Image from "next/image";
-import { parseCookies } from "nookies";
-
-export default function HamburgerMenu() {
- const { data: session } = useSession();
- const [isVisible, setIsVisible] = useState(false);
- const [fade, setFade] = useState(false);
-
- const [lang, setLang] = useState("en");
- const [cookie, setCookies] = useState(null);
-
- const handleShowClick = () => {
- setIsVisible(true);
- setFade(true);
- };
-
- const handleHideClick = () => {
- setIsVisible(false);
- setFade(false);
- };
-
- useEffect(() => {
- let lang = null;
- if (!cookie) {
- const cookie = parseCookies();
- lang = cookie.lang || null;
- setCookies(cookie);
- }
- if (lang === "en" || lang === null) {
- setLang("en");
- } else if (lang === "id") {
- setLang("id");
- }
- }, []);
- return (
- <>
- {!isVisible && (
- <button
- onClick={handleShowClick}
- className="fixed bottom-[30px] right-[20px] z-[100] flex h-[51px] w-[50px] cursor-pointer items-center justify-center rounded-[8px] bg-[#17171f] shadow-lg lg:hidden"
- id="bars"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- className="h-[42px] w-[61.5px] text-[#8BA0B2] fill-orange-500"
- viewBox="0 0 20 20"
- fill="currentColor"
- >
- <path
- fillRule="evenodd"
- d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
- clipRule="evenodd"
- />
- </svg>
- </button>
- )}
-
- {/* Mobile Menu */}
- <div
- className={`transition-all duration-150 ${
- fade ? "opacity-100" : "opacity-0"
- } z-50`}
- >
- {isVisible && session && (
- <Link
- href={`/${lang}/profile/${session?.user?.name}`}
- className="fixed lg:hidden bottom-[100px] w-[60px] h-[60px] flex items-center justify-center right-[20px] rounded-full z-50 bg-[#17171f]"
- >
- <Image
- src={session?.user.image.large}
- alt="user avatar"
- height={500}
- width={500}
- className="object-cover w-[60px] h-[60px] rounded-full"
- />
- </Link>
- )}
- {isVisible && (
- <div className="fixed bottom-[30px] right-[20px] z-50 flex h-[51px] w-[300px] items-center justify-center gap-8 rounded-[8px] text-[11px] bg-[#17171f] shadow-lg lg:hidden">
- <div className="grid grid-cols-4 place-items-center gap-6">
- <button className="group flex flex-col items-center">
- <Link href={`/${lang}/`} className="">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
- />
- </svg>
- </Link>
- <Link
- href={`/${lang}/`}
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- home
- </Link>
- </button>
- <button className="group flex flex-col items-center">
- <Link href={`/${lang}/about`}>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
- />
- </svg>
- </Link>
- <Link
- href={`/${lang}/about`}
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- about
- </Link>
- </button>
- <button className="group flex gap-[1.5px] flex-col items-center ">
- <div>
- <Link href={`/${lang}/search/anime`}>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
- />
- </svg>
- </Link>
- </div>
- <Link
- href={`/${lang}/search/anime`}
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- search
- </Link>
- </button>
- {session ? (
- <button
- onClick={() => signOut("AniListProvider")}
- className="group flex gap-[1.5px] flex-col items-center "
- >
- <div>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 96 960 960"
- className="group-hover:fill-action w-6 h-6 fill-txt"
- >
- <path d="M186.666 936q-27 0-46.833-19.833T120 869.334V282.666q0-27 19.833-46.833T186.666 216H474v66.666H186.666v586.668H474V936H186.666zm470.668-176.667l-47-48 102-102H370v-66.666h341.001l-102-102 46.999-48 184 184-182.666 182.666z"></path>
- </svg>
- </div>
- <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action">
- logout
- </h1>
- </button>
- ) : (
- <button
- onClick={() => signIn("AniListProvider")}
- className="group flex gap-[1.5px] flex-col items-center "
- >
- <div>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 96 960 960"
- className="group-hover:fill-action w-6 h-6 fill-txt mr-2"
- >
- <path d="M486 936v-66.666h287.334V282.666H486V216h287.334q27 0 46.833 19.833T840 282.666v586.668q0 27-19.833 46.833T773.334 936H486zm-78.666-176.667l-47-48 102-102H120v-66.666h341l-102-102 47-48 184 184-182.666 182.666z"></path>
- </svg>
- </div>
- <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action">
- login
- </h1>
- </button>
- )}
- </div>
- <button onClick={handleHideClick}>
- <svg
- width="20"
- height="21"
- className="fill-orange-500"
- viewBox="0 0 20 21"
- fill="none"
- xmlns="http://www.w3.org/2000/svg"
- >
- <rect
- x="2.44043"
- y="0.941467"
- width="23.5842"
- height="3.45134"
- rx="1.72567"
- transform="rotate(45 2.44043 0.941467)"
- />
- <rect
- x="19.1172"
- y="3.38196"
- width="23.5842"
- height="3.45134"
- rx="1.72567"
- transform="rotate(135 19.1172 3.38196)"
- />
- </svg>
- </button>
- </div>
- )}
- </div>
- </>
- );
-}
diff --git a/components/manga/panels/firstPanel.js b/components/manga/panels/firstPanel.js
index f1ee859..596fa58 100644
--- a/components/manga/panels/firstPanel.js
+++ b/components/manga/panels/firstPanel.js
@@ -4,10 +4,13 @@ import {
ArrowsPointingInIcon,
ChevronLeftIcon,
ChevronRightIcon,
+ PlusIcon,
+ MinusIcon,
} from "@heroicons/react/24/outline";
import Image from "next/image";
import { useRouter } from "next/router";
import { useAniList } from "../../../lib/anilist/useAnilist";
+import { getHeaders, getRandomId } from "@/utils/imageUtils";
export default function FirstPanel({
aniId,
@@ -26,14 +29,20 @@ export default function FirstPanel({
mobileVisible,
setMobileVisible,
setCurrentPage,
+ number,
+ mangadexId,
}) {
const { markProgress } = useAniList(session);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const imageRefs = useRef([]);
const scrollContainerRef = useRef();
+ const [imageQuality, setImageQuality] = useState(80);
+
const router = useRouter();
+ // console.log({ chapter });
+
useEffect(() => {
const handleScroll = () => {
const scrollTop = scrollContainerRef.current.scrollTop;
@@ -53,13 +62,17 @@ export default function FirstPanel({
}
}
- if (index === data.length - 3 && !hasRun.current) {
+ if (index === data?.length - 3 && !hasRun.current) {
if (session) {
+ if (aniId?.length > 6) return;
const currentChapter = chapter.chapters?.find(
(x) => x.id === currentId
);
if (currentChapter) {
- markProgress(aniId, currentChapter.number);
+ const chapterNumber =
+ currentChapter.number ??
+ chapter.chapters.indexOf(currentChapter) + 1;
+ markProgress(aniId, chapterNumber);
console.log("marking progress");
}
}
@@ -82,8 +95,12 @@ export default function FirstPanel({
});
}
};
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, session, chapter]);
+ // console.log({ imageQuality });
+
useEffect(() => {
if (scrollContainerRef.current && seekPage !== currentImageIndex) {
const targetImageRef = imageRefs.current[seekPage];
@@ -119,19 +136,26 @@ export default function FirstPanel({
{data && Array.isArray(data) && data?.length > 0 ? (
data.map((i, index) => (
<div
- key={i.url}
+ key={getRandomId()}
className="w-screen lg:h-auto lg:w-full"
ref={(el) => (imageRefs.current[index] = el)}
>
<Image
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
i.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({ Referer: i.headers.Referer })
- )}`}
- alt={i.index}
+ )}${
+ i?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(i?.headers)
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(chapter.providerId))
+ )}`
+ }`}
+ alt={index}
width={500}
height={500}
+ quality={imageQuality}
onClick={() => setMobileVisible(!mobileVisible)}
className="w-screen lg:w-full h-auto bg-[#bbb]"
/>
@@ -145,6 +169,26 @@ export default function FirstPanel({
)}
</div>
<div className="absolute hidden lg:flex bottom-5 left-5 gap-5">
+ {/* <button
+ type="button"
+ disabled={imageQuality >= 100}
+ onClick={() => {
+ setImageQuality((prev) => (prev <= 100 ? prev + 10 : prev));
+ }}
+ className="flex-center p-2 bg-secondary"
+ >
+ <PlusIcon className="w-5 h-5" />
+ </button>
+ <button
+ type="button"
+ disabled={imageQuality <= 10}
+ onClick={() => {
+ setImageQuality((prev) => (prev >= 10 ? prev - 10 : prev));
+ }}
+ className="flex-center p-2 bg-secondary"
+ >
+ <MinusIcon className="w-5 h-5" />
+ </button> */}
<span className="flex bg-secondary p-2 rounded-sm">
{visible ? (
<button type="button" onClick={() => setVisible(!visible)}>
@@ -168,7 +212,11 @@ export default function FirstPanel({
router.push(
`/en/manga/read/${
chapter.providerId
- }?id=${aniId}&chapterId=${encodeURIComponent(prevChapter)}`
+ }?id=${mangadexId}&chapterId=${encodeURIComponent(
+ prevChapter?.id
+ )}${aniId?.length > 6 ? "" : `&anilist=${aniId}`}&num=${
+ prevChapter?.number
+ }`
)
}
>
@@ -185,7 +233,11 @@ export default function FirstPanel({
router.push(
`/en/manga/read/${
chapter.providerId
- }?id=${aniId}&chapterId=${encodeURIComponent(nextChapter)}`
+ }?id=${mangadexId}&chapterId=${encodeURIComponent(
+ nextChapter?.id
+ )}${aniId?.length > 6 ? "" : `&anilist=${aniId}`}&num=${
+ nextChapter?.number
+ }`
)
}
>
@@ -195,7 +247,7 @@ export default function FirstPanel({
</div>
<span className="hidden lg:flex bg-secondary p-2 rounded-sm absolute bottom-5 right-5">{`Page ${
currentImageIndex + 1
- }/${data.length}`}</span>
+ }/${data?.length}`}</span>
</section>
);
}
diff --git a/components/manga/panels/secondPanel.js b/components/manga/panels/secondPanel.js
index 9323822..fa158b2 100644
--- a/components/manga/panels/secondPanel.js
+++ b/components/manga/panels/secondPanel.js
@@ -5,9 +5,11 @@ import {
ArrowsPointingInIcon,
} from "@heroicons/react/24/outline";
import { useAniList } from "../../../lib/anilist/useAnilist";
+import { getHeaders } from "@/utils/imageUtils";
export default function SecondPanel({
aniId,
+ chapterData,
data,
hasRun,
currentChapter,
@@ -17,6 +19,7 @@ export default function SecondPanel({
visible,
setVisible,
session,
+ providerId,
}) {
const [index, setIndex] = useState(0);
const [image, setImage] = useState(null);
@@ -26,6 +29,7 @@ export default function SecondPanel({
useEffect(() => {
setIndex(0);
setSeekPage(0);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, currentId]);
const seekToIndex = (newIndex) => {
@@ -41,6 +45,7 @@ export default function SecondPanel({
useEffect(() => {
seekToIndex(seekPage);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [seekPage]);
useEffect(() => {
@@ -63,13 +68,14 @@ export default function SecondPanel({
}
if (index + 1 >= image.length - 4 && !hasRun.current) {
- let chapterNumber = currentChapter?.number;
- if (chapterNumber % 1 !== 0) {
- // If it's a decimal, round it
- chapterNumber = Math.round(chapterNumber);
- }
+ const current = chapterData.chapters?.find(
+ (x) => x.id === currentChapter.id
+ );
+ const chapterNumber = chapterData.chapters.indexOf(current) + 1;
- markProgress(aniId, chapterNumber);
+ if (chapterNumber) {
+ markProgress(aniId, chapterNumber);
+ }
hasRun.current = true;
}
}
@@ -80,6 +86,7 @@ export default function SecondPanel({
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [index, image]);
const handleNext = () => {
@@ -90,10 +97,13 @@ export default function SecondPanel({
if (index + 1 >= image.length - 4 && !hasRun.current) {
console.log("marking progress");
- let chapterNumber = currentChapter?.number;
- if (chapterNumber % 1 !== 0) {
- // If it's a decimal, round it
- chapterNumber = Math.round(chapterNumber);
+ const current = chapterData.chapters?.find(
+ (x) => x.id === currentChapter.id
+ );
+ const chapterNumber = chapterData.chapters.indexOf(current) + 1;
+
+ if (chapterNumber) {
+ markProgress(aniId, chapterNumber);
}
markProgress(aniId, chapterNumber);
@@ -107,6 +117,7 @@ export default function SecondPanel({
setSeekPage(index - 2);
}
};
+
return (
<div className="flex-grow h-screen">
<div className="flex items-center w-full relative group">
@@ -127,11 +138,17 @@ export default function SecondPanel({
className="w-1/2 h-screen object-contain"
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
image[image.length - index - 2]?.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({
- Referer: image[image.length - index - 2]?.headers.Referer,
- })
- )}`}
+ )}${
+ image[image.length - index - 2]?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(
+ image[image.length - index - 2]?.headers
+ )
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(providerId))
+ )}`
+ }`}
alt="Manga Page"
/>
)}
@@ -142,11 +159,15 @@ export default function SecondPanel({
className="w-1/2 h-screen object-contain"
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
image[image.length - index - 1]?.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({
- Referer: image[image.length - index - 1]?.headers.Referer,
- })
- )}`}
+ )}${
+ image[image.length - index - 1]?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(image[image.length - index - 1]?.headers)
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(providerId))
+ )}`
+ }`}
alt="Manga Page"
/>
</div>
diff --git a/components/manga/panels/thirdPanel.js b/components/manga/panels/thirdPanel.js
index d402f07..f13b49d 100644
--- a/components/manga/panels/thirdPanel.js
+++ b/components/manga/panels/thirdPanel.js
@@ -5,10 +5,12 @@ import {
ArrowsPointingInIcon,
} from "@heroicons/react/24/outline";
import { useAniList } from "../../../lib/anilist/useAnilist";
+import { getHeaders } from "@/utils/imageUtils";
export default function ThirdPanel({
aniId,
data,
+ chapterData,
hasRun,
currentId,
currentChapter,
@@ -20,6 +22,7 @@ export default function ThirdPanel({
scaleImg,
setMobileVisible,
mobileVisible,
+ providerId,
}) {
const [index, setIndex] = useState(0);
const [image, setImage] = useState(null);
@@ -28,6 +31,7 @@ export default function ThirdPanel({
useEffect(() => {
setIndex(0);
setSeekPage(0);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, currentId]);
const seekToIndex = (newIndex) => {
@@ -39,6 +43,7 @@ export default function ThirdPanel({
useEffect(() => {
seekToIndex(seekPage);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [seekPage]);
useEffect(() => {
@@ -60,13 +65,14 @@ export default function ThirdPanel({
setSeekPage(index + 1);
}
if (index + 1 >= image.length - 2 && !hasRun.current) {
- let chapterNumber = currentChapter?.number;
- if (chapterNumber % 1 !== 0) {
- // If it's a decimal, round it
- chapterNumber = Math.round(chapterNumber);
- }
+ const current = chapterData.chapters?.find(
+ (x) => x.id === currentChapter.id
+ );
+ const chapterNumber = chapterData.chapters.indexOf(current) + 1;
- markProgress(aniId, chapterNumber);
+ if (chapterNumber) {
+ markProgress(aniId, chapterNumber);
+ }
hasRun.current = true;
}
}
@@ -77,6 +83,8 @@ export default function ThirdPanel({
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [index, image]);
const handleNext = () => {
@@ -85,13 +93,15 @@ export default function ThirdPanel({
setSeekPage(index + 1);
}
if (index + 1 >= image.length - 2 && !hasRun.current) {
- let chapterNumber = currentChapter?.number;
- if (chapterNumber % 1 !== 0) {
- // If it's a decimal, round it
- chapterNumber = Math.round(chapterNumber);
+ const current = chapterData.chapters?.find(
+ (x) => x.id === currentChapter.id
+ );
+ const chapterNumber = chapterData.chapters.indexOf(current) + 1;
+
+ if (chapterNumber) {
+ markProgress(aniId, chapterNumber);
}
- markProgress(aniId, chapterNumber);
hasRun.current = true;
}
};
@@ -119,11 +129,15 @@ export default function ThirdPanel({
onClick={() => setMobileVisible(!mobileVisible)}
src={`https://api.consumet.org/utils/image-proxy?url=${encodeURIComponent(
image[image.length - index - 1]?.url
- )}&headers=${encodeURIComponent(
- JSON.stringify({
- Referer: image[image.length - index - 1]?.headers.Referer,
- })
- )}`}
+ )}${
+ image[image.length - index - 1]?.headers?.Referer
+ ? `&headers=${encodeURIComponent(
+ JSON.stringify(image[image.length - index - 1]?.headers)
+ )}`
+ : `&headers=${encodeURIComponent(
+ JSON.stringify(getHeaders(providerId))
+ )}`
+ }`}
alt="Manga Page"
style={{
transform: `scale(${scaleImg})`,
diff --git a/components/manga/rightBar.js b/components/manga/rightBar.js
index 82d577d..9672fc4 100644
--- a/components/manga/rightBar.js
+++ b/components/manga/rightBar.js
@@ -4,16 +4,15 @@ import {
} from "@heroicons/react/24/outline";
import { useEffect, useState } from "react";
import { useAniList } from "../../lib/anilist/useAnilist";
-import { toast } from "react-toastify";
import AniList from "../media/aniList";
import { signIn } from "next-auth/react";
+import { toast } from "sonner";
export default function RightBar({
id,
hasRun,
session,
data,
- error,
currentChapter,
paddingX,
setPaddingX,
@@ -47,19 +46,13 @@ export default function RightBar({
markProgress(id, progress, status, volumeProgress);
hasRun.current = true;
} else {
- toast.error("Progress must be a whole number!", {
- position: "bottom-right",
- autoClose: 5000,
- hideProgressBar: true,
- closeOnClick: false,
- pauseOnHover: true,
- draggable: true,
- theme: "colored",
- });
+ toast.error("Progress must be a whole number!");
}
}
};
+ // console.log({ id });
+
const changeMode = (e) => {
setLayout(Number(e.target.value));
// console.log(e.target.value);
@@ -129,63 +122,72 @@ export default function RightBar({
</button>
</div>
</div>
+ {/* <div className="flex flex-col gap-3 w-full">
+ <h1 className="font-karla font-bold xl:text-lg">Set Quality</h1>
+ </div> */}
<div className="flex flex-col gap-3 w-full">
<h1 className="font-karla font-bold xl:text-lg">Tracking</h1>
{session ? (
- <div className="flex flex-col gap-2">
- <div className="space-y-1">
- <label className="font-karla font-semibold text-gray-500 text-xs">
- Status
- </label>
- <div className="relative">
- <select
- onChange={(e) => setStatus(e.target.value)}
- className="w-full px-2 py-1 font-karla rounded-md bg-[#161617] appearance-none text-sm"
- >
- <option value="CURRENT">Reading</option>
- <option value="PLANNING">Plan to Read</option>
- <option value="COMPLETED">Completed</option>
- <option value="REPEATING">Rereading</option>
- <option value="PAUSED">Paused</option>
- <option value="DROPPED">Dropped</option>
- </select>
- <ChevronDownIcon className="w-5 h-5 text-white absolute inset-0 my-auto mx-52" />
+ id?.length > 6 ? (
+ <p className="flex-center w-full py-2 font-karla">
+ Not available on AniList
+ </p>
+ ) : (
+ <div className="flex flex-col gap-2">
+ <div className="space-y-1">
+ <label className="font-karla font-semibold text-gray-500 text-xs">
+ Status
+ </label>
+ <div className="relative">
+ <select
+ onChange={(e) => setStatus(e.target.value)}
+ className="w-full px-2 py-1 font-karla rounded-md bg-[#161617] appearance-none text-sm"
+ >
+ <option value="CURRENT">Reading</option>
+ <option value="PLANNING">Plan to Read</option>
+ <option value="COMPLETED">Completed</option>
+ <option value="REPEATING">Rereading</option>
+ <option value="PAUSED">Paused</option>
+ <option value="DROPPED">Dropped</option>
+ </select>
+ <ChevronDownIcon className="w-5 h-5 text-white absolute inset-0 my-auto mx-52" />
+ </div>
</div>
+ <div className="space-y-1">
+ <label className="font-karla font-semibold text-gray-500 text-xs">
+ Chapter Progress
+ </label>
+ <input
+ id="chapter-progress"
+ type="number"
+ placeholder="0"
+ min={0}
+ value={progress}
+ onChange={(e) => setProgress(e.target.value)}
+ className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm"
+ />
+ </div>
+ <div className="space-y-1">
+ <label className="font-karla font-semibold text-gray-500 text-xs">
+ Volume Progress
+ </label>
+ <input
+ type="number"
+ placeholder="0"
+ min={0}
+ onChange={(e) => setVolumeProgress(e.target.value)}
+ className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm"
+ />
+ </div>
+ <button
+ type="button"
+ onClick={saveProgress}
+ className="w-full bg-[#424245] py-1 my-5 rounded-md text-white text-sm xl:text-base shadow-md font-karla font-semibold"
+ >
+ Save Progress
+ </button>
</div>
- <div className="space-y-1">
- <label className="font-karla font-semibold text-gray-500 text-xs">
- Chapter Progress
- </label>
- <input
- id="chapter-progress"
- type="number"
- placeholder="0"
- min={0}
- value={progress}
- onChange={(e) => setProgress(e.target.value)}
- className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm"
- />
- </div>
- <div className="space-y-1">
- <label className="font-karla font-semibold text-gray-500 text-xs">
- Volume Progress
- </label>
- <input
- type="number"
- placeholder="0"
- min={0}
- onChange={(e) => setVolumeProgress(e.target.value)}
- className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm"
- />
- </div>
- <button
- type="button"
- onClick={saveProgress}
- className="w-full bg-[#424245] py-1 my-5 rounded-md text-white text-sm xl:text-base shadow-md font-karla font-semibold"
- >
- Save Progress
- </button>
- </div>
+ )
) : (
<button
type="button"
diff --git a/components/search/searchByImage.js b/components/search/searchByImage.js
new file mode 100644
index 0000000..f95c2ad
--- /dev/null
+++ b/components/search/searchByImage.js
@@ -0,0 +1,119 @@
+import { PhotoIcon } from "@heroicons/react/24/outline";
+import { useRouter } from "next/router";
+import React, { useEffect } from "react";
+import { toast } from "sonner";
+
+export default function SearchByImage({
+ searchPalette = false,
+ setIsOpen,
+ setData,
+ setMedia,
+}) {
+ const router = useRouter();
+
+ async function findImage(formData) {
+ const response = new Promise((resolve, reject) => {
+ fetch("https://api.trace.moe/search?anilistInfo", {
+ method: "POST",
+ body: formData,
+ })
+ .then((resp) => {
+ resolve(resp.json());
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+
+ toast.promise(response, {
+ loading: "Finding episodes...",
+ success: `Episodes found!`,
+ error: "Error",
+ });
+
+ response
+ .then((data) => {
+ if (data?.result?.length > 0) {
+ const id = data.result[0].anilist.id;
+ const datas = data.result.filter((i) => i.anilist.isAdult === false);
+ if (setData) setData(datas);
+ if (searchPalette) router.push(`/en/anime/${id}`);
+ if (setIsOpen) setIsOpen(false);
+ if (setMedia) setMedia();
+ }
+ })
+ .catch((error) => {
+ console.error("Error:", error);
+ });
+ }
+
+ const handleImageSelect = async (e) => {
+ const selectedImage = e.target.files[0];
+
+ if (selectedImage) {
+ const formData = new FormData();
+ formData.append("image", selectedImage);
+
+ try {
+ await findImage(formData);
+ } catch (error) {
+ console.error("An error occurred:", error);
+ }
+ }
+ };
+
+ useEffect(() => {
+ // Add a global event listener for the paste event
+ const handlePaste = async (e) => {
+ e.preventDefault();
+
+ const items = e.clipboardData.items;
+
+ for (let i = 0; i < items.length; i++) {
+ if (items[i].type.indexOf("image") !== -1) {
+ const blob = items[i].getAsFile();
+
+ // Create a FormData object and append the pasted image
+ const formData = new FormData();
+ formData.append("image", blob);
+
+ try {
+ // Send the pasted image to your API for processing
+ await findImage(formData);
+ } catch (error) {
+ console.error("An error occurred:", error);
+ }
+ break; // Stop after finding the first image
+ }
+ }
+ };
+
+ // Add the event listener to the document
+ document.addEventListener("paste", handlePaste);
+
+ // Clean up the event listener when the component unmounts
+ return () => {
+ document.removeEventListener("paste", handlePaste);
+ };
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+ <div>
+ <label
+ className={`${
+ searchPalette ? "w-9 h-9" : "py-2 px-2"
+ } bg-secondary rounded flex justify-center items-center cursor-pointer hover:bg-opacity-75 transition-all duration-100 group`}
+ >
+ <PhotoIcon className="w-6 h-6" />
+ <input
+ type="file"
+ name="image"
+ onChange={handleImageSelect}
+ className="hidden"
+ />
+ </label>
+ </div>
+ );
+}
diff --git a/components/searchPalette.js b/components/searchPalette.js
index 38a0bc0..10b9003 100644
--- a/components/searchPalette.js
+++ b/components/searchPalette.js
@@ -1,4 +1,4 @@
-import { Fragment, useEffect, useState } from "react";
+import { Fragment, useEffect, useRef, useState } from "react";
import { Combobox, Dialog, Menu, Transition } from "@headlessui/react";
import useDebounce from "../lib/hooks/useDebounce";
import Image from "next/image";
@@ -8,6 +8,7 @@ import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
import { BookOpenIcon, PlayIcon } from "@heroicons/react/20/solid";
import { useAniList } from "../lib/anilist/useAnilist";
import { getFormat } from "../utils/getFormat";
+import SearchByImage from "./search/searchByImage";
export default function SearchPalette() {
const { isOpen, setIsOpen } = useSearch();
@@ -21,6 +22,7 @@ export default function SearchPalette() {
const [nextPage, setNextPage] = useState(false);
+ let focusInput = useRef(null);
const router = useRouter();
function closeModal() {
@@ -44,6 +46,7 @@ export default function SearchPalette() {
useEffect(() => {
advance();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [debounceSearch, type]);
useEffect(() => {
@@ -62,11 +65,17 @@ export default function SearchPalette() {
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<Transition appear show={isOpen} as={Fragment}>
- <Dialog as="div" className="relative z-[6969]" onClose={closeModal}>
+ <Dialog
+ as="div"
+ className="relative z-[6969]"
+ initialFocus={focusInput}
+ onClose={closeModal}
+ >
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
@@ -112,13 +121,13 @@ export default function SearchPalette() {
<span>S</span>
</div>
</div>
- <div>
+ <div className="flex gap-1 items-center">
<Menu
as="div"
className="relative inline-block text-left"
>
<div>
- <Menu.Button className="capitalize bg-secondary inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-medium text-white hover:bg-opacity-80 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
+ <Menu.Button className="capitalize bg-secondary inline-flex w-full justify-center rounded px-3 py-2 text-sm font-medium text-white hover:bg-opacity-80 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
{type.toLowerCase()}
<ChevronDownIcon
className="ml-2 -mr-1 h-5 w-5 text-violet-200 hover:text-violet-100"
@@ -171,10 +180,15 @@ export default function SearchPalette() {
</Menu.Items>
</Transition>
</Menu>
+ <SearchByImage
+ searchPalette={true}
+ setIsOpen={setIsOpen}
+ />
</div>
</div>
<div className="flex items-center text-base font-medium rounded bg-secondary">
<Combobox.Input
+ ref={focusInput}
className="p-5 text-white w-full bg-transparent border-0 outline-none"
placeholder="Search something..."
onChange={(event) => setQuery(event.target.value)}
diff --git a/components/secret.js b/components/secret.js
new file mode 100644
index 0000000..782fcf5
--- /dev/null
+++ b/components/secret.js
@@ -0,0 +1,36 @@
+import { useEffect, useState } from "react";
+
+export default function SecretPage({ cheatCode, onCheatCodeEntered }) {
+ const [typedCode, setTypedCode] = useState("");
+ const [timer, setTimer] = useState(null);
+
+ const handleKeyPress = (e) => {
+ const newTypedCode = typedCode + e.key;
+
+ if (newTypedCode === cheatCode) {
+ onCheatCodeEntered();
+ setTypedCode("");
+ } else {
+ setTypedCode(newTypedCode);
+
+ // Reset the timer if the user stops typing for 2 seconds
+ clearTimeout(timer);
+ const newTimer = setTimeout(() => {
+ setTypedCode("");
+ }, 2000);
+ setTimer(newTimer);
+ }
+ };
+
+ useEffect(() => {
+ window.addEventListener("keydown", handleKeyPress);
+
+ return () => {
+ window.removeEventListener("keydown", handleKeyPress);
+ };
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [typedCode]);
+
+ return;
+}
diff --git a/components/shared/NavBar.js b/components/shared/NavBar.js
index 7bbd617..034a06b 100644
--- a/components/shared/NavBar.js
+++ b/components/shared/NavBar.js
@@ -56,7 +56,7 @@ export function NewNavbar({
shrink ? "py-1" : `${paddingY}`
}`
: `${paddingY}`
- } transition-all duration-200 ease-linear`}
+ } transition-all duration-200 ease-linear`}
>
<div
className={`flex items-center justify-between mx-auto ${
@@ -83,6 +83,7 @@ export function NewNavbar({
>
<ArrowLeftIcon className="w-full h-full" />
</button>
+
<span
className={`font-inter font-semibold w-[50%] line-clamp-1 select-none ${
scrollPosition?.y >= scrollP + 80
@@ -196,7 +197,7 @@ export function NewNavbar({
// title={sessions ? "Go to Profile" : "Login With AniList"}
> */}
{session ? (
- <div className="w-7 h-7 relative flex flex-col items-center group">
+ <div className="w-7 h-7 relative flex flex-col items-center group shrink-0">
<button
type="button"
onClick={() =>
@@ -233,7 +234,7 @@ export function NewNavbar({
type="button"
onClick={() => signIn("AniListProvider")}
title="Login With AniList"
- className="w-7 h-7 bg-white/30 rounded-full overflow-hidden"
+ className="w-7 h-7 bg-white/30 rounded-full overflow-hidden shrink-0"
>
<UserIcon className="w-full h-full translate-y-1" />
</button>
diff --git a/components/shared/bugReport.js b/components/shared/bugReport.js
index 9b99016..f6bd9f1 100644
--- a/components/shared/bugReport.js
+++ b/components/shared/bugReport.js
@@ -1,7 +1,7 @@
import { Fragment, useState } from "react";
import { Dialog, Listbox, Transition } from "@headlessui/react";
import { CheckIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
-import { toast } from "react-toastify";
+import { toast } from "sonner";
const severityOptions = [
{ id: 1, name: "Low" },
@@ -42,17 +42,11 @@ const BugReportForm = ({ isOpen, setIsOpen }) => {
});
const json = await res.json();
- toast.success(json.message, {
- hideProgressBar: true,
- theme: "colored",
- });
+ toast.success(json.message);
closeModal();
} catch (err) {
console.log(err);
- toast.error("Something went wrong: " + err.message, {
- hideProgressBar: true,
- theme: "colored",
- });
+ toast.error("Something went wrong: " + err.message);
}
};
diff --git a/components/shared/footer.js b/components/shared/footer.js
index 91af5a8..0e19f13 100644
--- a/components/shared/footer.js
+++ b/components/shared/footer.js
@@ -28,6 +28,8 @@ function Footer() {
setLang("id");
setChecked(true);
}
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function switchLang() {
diff --git a/components/watch/player/artplayer.js b/components/watch/player/artplayer.js
index 4ae8aa1..666c103 100644
--- a/components/watch/player/artplayer.js
+++ b/components/watch/player/artplayer.js
@@ -46,7 +46,7 @@ export default function NewPlayer({
customType: {
m3u8: playM3u8,
},
- ...(provider === "zoro" && {
+ ...(subtitles?.length > 0 && {
subtitle: {
url: `${defSub}`,
// type: "vtt",
@@ -131,7 +131,7 @@ export default function NewPlayer({
return item.html;
},
},
- provider === "zoro" && {
+ subtitles?.length > 0 && {
html: "Subtitles",
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 24 24"><path fill="currentColor" d="M4 20q-.825 0-1.413-.588T2 18V6q0-.825.588-1.413T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.588 1.413T20 20H4Zm2-4h8v-2H6v2Zm10 0h2v-2h-2v2ZM6 12h2v-2H6v2Zm4 0h8v-2h-8v2Z"></path></svg>',
width: 300,
@@ -261,7 +261,7 @@ export default function NewPlayer({
index: 11,
position: "right",
tooltip: "Theater (t)",
- html: '<p class="theater"><svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 20 20"><path fill="currentColor" d="M19 3H1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm-1 12H2V5h16v10z"></path></svg></p>',
+ html: '<i class="theater"><svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 20 20"><path fill="currentColor" d="M19 3H1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm-1 12H2V5h16v10z"></path></svg></i>',
click: function (...args) {
setPlayerState((prev) => ({
...prev,
@@ -379,6 +379,8 @@ export default function NewPlayer({
art.destroy(false);
}
};
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return <div ref={artRef} {...rest}></div>;
diff --git a/components/watch/player/component/controls/subtitle.js b/components/watch/player/component/controls/subtitle.js
deleted file mode 100644
index 02075f7..0000000
--- a/components/watch/player/component/controls/subtitle.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { useState } from "react";
-
-export default function getSubtitles() {}
diff --git a/components/watch/player/playerComponent.js b/components/watch/player/playerComponent.js
index 37c5810..665919b 100644
--- a/components/watch/player/playerComponent.js
+++ b/components/watch/player/playerComponent.js
@@ -4,6 +4,7 @@ import { icons } from "./component/overlay";
import { useWatchProvider } from "@/lib/context/watchPageProvider";
import { useRouter } from "next/router";
import { useAniList } from "@/lib/anilist/useAnilist";
+import Loading from "@/components/shared/loading";
export function calculateAspectRatio(width, height) {
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
@@ -74,20 +75,18 @@ export default function PlayerComponent({
setResolution(resol);
}
- if (provider === "zoro") {
- const size = fontSize.map((i) => {
- const isDefault = !sub ? i.html === "Small" : i.html === sub?.html;
- return {
- ...(isDefault && { default: true }),
- html: i.html,
- size: i.size,
- };
- });
+ const size = fontSize.map((i) => {
+ const isDefault = !sub ? i.html === "Small" : i.html === sub?.html;
+ return {
+ ...(isDefault && { default: true }),
+ html: i.html,
+ size: i.size,
+ };
+ });
- const defSize = size?.find((i) => i?.default === true);
- setDefSize(defSize);
- setSubSize(size);
- }
+ const defSize = size?.find((i) => i?.default === true);
+ setDefSize(defSize);
+ setSubSize(size);
async function compiler() {
try {
@@ -114,19 +113,26 @@ export default function PlayerComponent({
setUrl(defSource.url);
}
- if (provider === "zoro") {
- const subtitle = data?.subtitles
- .filter((subtitle) => subtitle.lang !== "Thumbnails")
- .map((subtitle) => {
- const isEnglish = subtitle.lang === "English";
- return {
- ...(isEnglish && { default: true }),
- url: subtitle.url,
- html: `${subtitle.lang}`,
- };
- });
+ const subtitle = data?.subtitles
+ ?.filter(
+ (subtitle) =>
+ subtitle.lang !== "Thumbnails" && subtitle.lang !== "thumbnails"
+ )
+ ?.map((subtitle) => {
+ const isEnglish =
+ subtitle.lang === "English" ||
+ subtitle.lang === "English / English (US)";
+ return {
+ ...(isEnglish && { default: true }),
+ url: subtitle.url,
+ html: `${subtitle.lang}`,
+ };
+ });
- const defSub = data?.subtitles.find((i) => i.lang === "English");
+ if (subtitle) {
+ const defSub = data?.subtitles.find(
+ (i) => i.lang === "English" || i.lang === "English / English (US)"
+ );
setDefSub(defSub?.url);
@@ -162,6 +168,8 @@ export default function PlayerComponent({
setSubtitle([]);
setLoading(true);
};
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [provider, data]);
/**
@@ -171,6 +179,17 @@ export default function PlayerComponent({
art.on("ready", () => {
const autoplay = localStorage.getItem("autoplay_video") || false;
+ // check media queries for mobile devices
+ const isMobile = window.matchMedia("(max-width: 768px)").matches;
+
+ // console.log(art.fullscreen);
+
+ if (isMobile) {
+ art.controls.remove("theater-button");
+ // art.controls.remove("fast-rewind");
+ // art.controls.remove("fast-forward");
+ }
+
if (autoplay === "true" || autoplay === true) {
if (playerState.currentTime === 0) {
art.play();
@@ -465,10 +484,13 @@ export default function PlayerComponent({
style={{ aspectRatio: aspectRatio }}
>
<div className="flex-center w-full h-full">
+ {!data?.error && !url && (
+ <div className="flex-center w-full h-full">
+ <Loading />
+ </div>
+ )}
{!error ? (
- !loading &&
- track &&
- url && (
+ !loading && track && url && !data?.error ? (
<NewPlayer
playerRef={playerRef}
res={resolution}
@@ -486,6 +508,12 @@ export default function PlayerComponent({
height: "100%",
}}
/>
+ ) : (
+ <p className="text-center">
+ {data?.status === 404 && "Not Found"}
+ <br />
+ {data?.error}
+ </p>
)
) : (
<p className="text-center">
diff --git a/components/watch/player/utils/getZoroSource.js b/components/watch/player/utils/getZoroSource.js
deleted file mode 100644
index e69de29..0000000
--- a/components/watch/player/utils/getZoroSource.js
+++ /dev/null
diff --git a/components/watch/secondary/episodeLists.js b/components/watch/secondary/episodeLists.js
index 41f1a76..485b43e 100644
--- a/components/watch/secondary/episodeLists.js
+++ b/components/watch/secondary/episodeLists.js
@@ -1,6 +1,8 @@
import Skeleton from "react-loading-skeleton";
import Image from "next/image";
import Link from "next/link";
+import { ChevronDownIcon } from "@heroicons/react/24/outline";
+import { useRouter } from "next/router";
export default function EpisodeLists({
info,
@@ -9,13 +11,56 @@ export default function EpisodeLists({
watchId,
episode,
artStorage,
+ track,
dub,
}) {
const progress = info.mediaListEntry?.progress;
+ const router = useRouter();
+
return (
<div className="w-screen lg:max-w-sm xl:max-w-lg">
- <h1 className="text-xl font-karla pl-5 pb-5 font-semibold">Up Next</h1>
+ <div className="flex gap-4 pl-5 pb-5">
+ <button
+ disabled={!track?.next}
+ onClick={() => {
+ router.push(
+ `/en/anime/watch/${info.id}/${providerId}?id=${
+ track?.next?.id
+ }&num=${track?.next?.number}${dub ? `&dub=${dub}` : ""}`
+ );
+ }}
+ className="text-xl font-karla font-semibold"
+ >
+ Next Episode {">"}
+ </button>
+ {episode && (
+ <div className="relative flex gap-2 items-center group">
+ <select
+ value={track?.playing?.number}
+ onChange={(e) => {
+ const selectedEpisode = episode.find(
+ (episode) => episode.number === parseInt(e.target.value)
+ );
+
+ router.push(
+ `/en/anime/watch/${info.id}/${providerId}?id=${
+ selectedEpisode.id
+ }&num=${selectedEpisode.number}${dub ? `&dub=${dub}` : ""}`
+ );
+ }}
+ className="flex items-center text-sm gap-5 rounded-[3px] bg-secondary py-1 px-3 pr-8 font-karla appearance-none cursor-pointer outline-none focus:ring-1 focus:ring-action group-hover:ring-1 group-hover:ring-action"
+ >
+ {episode?.map((x) => (
+ <option key={x.id} value={x.number}>
+ Episode {x.number}
+ </option>
+ ))}
+ </select>
+ <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" />
+ </div>
+ )}
+ </div>
<div className="flex flex-col gap-5 lg:pl-5 py-2 scrollbar-thin px-2 scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full">
{episode && episode.length > 0 ? (
map?.some(