From f801f8f422954b884a6541321dba0669ee9d6173 Mon Sep 17 00:00:00 2001 From: Factiven Date: Sun, 22 Oct 2023 19:43:17 +0700 Subject: Update v4.2.0 (#93) --- components/admin/dashboard/index.js | 220 +++++++++++++++---- components/admin/meta/AppendMeta.js | 3 +- components/anime/episode.js | 34 ++- components/anime/mobile/topSection.js | 68 ++++-- components/anime/viewMode/listMode.js | 2 +- components/home/content.js | 17 +- components/home/genres.js | 1 + components/home/schedule.js | 27 ++- components/listEditor.js | 53 ++--- components/manga/chapters.js | 236 ++++++++++++--------- components/manga/info/mobile/mobileButton.js | 39 ---- components/manga/info/mobile/topMobile.js | 16 -- components/manga/info/topSection.js | 107 ---------- components/manga/leftBar.js | 56 +++-- components/manga/mobile/bottomBar.js | 36 +++- components/manga/mobile/hamburgerMenu.js | 228 -------------------- components/manga/panels/firstPanel.js | 72 ++++++- components/manga/panels/secondPanel.js | 61 ++++-- components/manga/panels/thirdPanel.js | 46 ++-- components/manga/rightBar.js | 128 +++++------ components/search/searchByImage.js | 119 +++++++++++ components/searchPalette.js | 22 +- components/secret.js | 36 ++++ components/shared/NavBar.js | 7 +- components/shared/bugReport.js | 12 +- components/shared/footer.js | 2 + components/watch/player/artplayer.js | 8 +- .../watch/player/component/controls/subtitle.js | 3 - components/watch/player/playerComponent.js | 84 +++++--- components/watch/player/utils/getZoroSource.js | 0 components/watch/secondary/episodeLists.js | 47 +++- 31 files changed, 975 insertions(+), 815 deletions(-) delete mode 100644 components/manga/info/mobile/mobileButton.js delete mode 100644 components/manga/info/mobile/topMobile.js delete mode 100644 components/manga/info/topSection.js delete mode 100644 components/manga/mobile/hamburgerMenu.js create mode 100644 components/search/searchByImage.js create mode 100644 components/secret.js delete mode 100644 components/watch/player/component/controls/subtitle.js delete mode 100644 components/watch/player/utils/getZoroSource.js (limited to 'components') 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 (
@@ -39,7 +118,21 @@ export default function AdminDashboard({
-

Broadcast

+

+ Broadcast + + + + +

@@ -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" />
- +
+ + +
{unixTimestamp && (

@@ -95,40 +196,85 @@ export default function AdminDashboard({ {report?.map((i, index) => (

- {i.desc}{" "} - {i.severity === "Low" && ( - - {/* */} - - - )} - {i.severity === "Medium" && ( - - {/* */} - - - )} - {i.severity === "High" && ( - - {/* */} - - - )} - {i.severity === "Critical" && ( - - - + + {i.desc}{" "} + + - )} + +
+ {i.severity === "Low" && ( + + {/* */} + + + )} + {i.severity === "Medium" && ( + + {/* */} + + + )} + {i.severity === "High" && ( + + {/* */} + + + )} + {i.severity === "Critical" && ( + + + + + )} + +
))}
-
a
); } 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 }) {

query-image 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({
poster anime
-

- {info?.season?.toLowerCase()} {info.seasonYear} +

+ {info?.season?.toLowerCase() || getMonth(info?.startDate?.month)}{" "} + {info.seasonYear || info?.startDate?.year}

{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`} > - + {isAnime ? ( + + ) : ( + + )} {progress > 0 ? ( statuses?.value === "COMPLETED" ? ( - "Rewatch" + isAnime ? ( + "Rewatch" + ) : ( + "Reread" + ) ) : !watchUrl && info?.nextAiringEpisode ? ( {convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "} @@ -100,8 +116,10 @@ export default function DetailTop({ ) : ( "Continue" ) - ) : ( + ) : isAnime ? ( "Watch Now" + ) : ( + "Read Now" )}
@@ -121,14 +139,14 @@ export default function DetailTop({ onClick={handleShareClick} > - Share Anime + Share {isAnime ? "Anime" : "Manga"} @@ -156,18 +174,24 @@ export default function DetailTop({ @@ -287,3 +313,11 @@ export default function DetailTop({
); } + +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`} >
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 (
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 }) {

Don't miss out!

-
+
-
-

Coming Up Next!

-
+
+

+ Coming Up Next! +

+
{data.title.romaji || data.title.english || data.title.native}
-

+ {data.title.romaji || data.title.english || data.title.native} -

+
{data.bannerImage ? ( banner next anime )}
{/* Countdown Timer */} -
+
{/* Countdown Timer */}
{day} 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 = ({
- {image && ( + {info?.bannerImage && (
image image setStatus(e.target.value)} className="rounded-sm px-2 py-1 bg-[#363642] w-[50%] sm:w-[150px] text-sm sm:text-base" > - + - +
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 ( -
-
- -
+
+
+

+ Chapters +

+
- +
-
- -
- {displayedPageNumbers.map((number, index) => - number === "..." ? ( - - ... - - ) : ( + +
+
+ {currentChapters.map((chapter, index) => { + const isRead = chapter.number <= data?.mediaListEntry?.progress; + return ( + 6 ? "" : `&anilist=${data.id}`}&num=${ + chapter.number + }`} + className={`flex gap-3 py-4 hover:bg-secondary odd:bg-secondary/30 even:bg-primary`} + > +
+ + {chapter.number} + +

+ {chapter.title || `Chapter ${chapter.number}`} +

+

+ {selectedProvider} +

+
+ + ); + })} +
+ +
+
+

+ Showing{" "} + {indexOfFirstChapter + 1} to{" "} + + {indexOfLastChapter > chapters.length + ? chapters.length + : indexOfLastChapter} + {" "} + of {chapters.length} chapters +

+
+
+
- -
-
- {currentChapters.map((chapter, index) => { - const isRead = chapter.number <= userManga?.progress; - return ( -
- + {displayedPageNumbers.map((pageNumber, index) => ( + + ))} +
+
- ); - })} + Next +
+
); 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 ( -
- - -
- -
- -
- ); -} 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 ( -
- cover image -
-
- ); -} 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 ( -
- -
- cover image - -
- - -
- -
- -
-
-
-
-

- {info.title?.romaji || info.title?.english || info.title?.native} -

- - {slicedGenre && - slicedGenre.map((genre, index) => { - return ( -
- {genre} - {index < slicedGenre?.length - 1 && ( - - )} -
- ); - })} -
-
- - - -
- {/* Description */} -
-

-

-
-
-
-
- ); -} 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 (
@@ -37,23 +46,27 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {

Chapters

- {data?.chapters?.map((x) => { + {data?.chapters?.map((x, index) => { return (
6 ? "" : `&anilist=${info?.id}` + }&num=${x.number}`} className="" >

- {x.number}.{" "} - {x.title} + + {x.number || index + 1}. + {" "} + {x.title || `Chapter ${x.number || index + 1}`}

@@ -69,28 +82,37 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {
{Array.isArray(page) ? (
- {page?.map((x) => { + {page?.map((x, index) => { return (
setSeekPage(x.index)} + onClick={() => setSeekPage(index)} > chapter image -

Page {x.index + 1}

+

Page {index + 1}

); @@ -98,7 +120,7 @@ export function LeftBar({ data, page, info, currentId, setSeekPage }) {
) : (
-

{page.error || "No Pages."}

+

{page?.error || "No Pages."}

)}
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 (
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({
- {`${currentPage}/${page.length}`} + {`${currentPage}/${data?.length}`}
{openPage && (
- {Array.isArray(page) ? ( - page.map((x) => { + {Array.isArray(data) ? ( + data.map((x, index) => { + const indx = index + 1; return (
{ - 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 && ( - - )} - - {/* Mobile Menu */} -
- {isVisible && session && ( - - user avatar - - )} - {isVisible && ( -
-
- - - - {session ? ( - - ) : ( - - )} -
- -
- )} -
- - ); -} 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) => (
(imageRefs.current[index] = el)} > {i.index} setMobileVisible(!mobileVisible)} className="w-screen lg:w-full h-auto bg-[#bbb]" /> @@ -145,6 +169,26 @@ export default function FirstPanel({ )}
+ {/* + */} {visible ? (
{`Page ${ currentImageIndex + 1 - }/${data.length}`} + }/${data?.length}`} ); } 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 (
@@ -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" />
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({
+ {/*
+

Set Quality

+
*/}

Tracking

{session ? ( -
-
- -
- - + id?.length > 6 ? ( +

+ Not available on AniList +

+ ) : ( +
+
+ +
+ + +
+
+ + setProgress(e.target.value)} + className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm" + /> +
+
+ + setVolumeProgress(e.target.value)} + className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm" + /> +
+
-
- - setProgress(e.target.value)} - className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm" - /> -
-
- - setVolumeProgress(e.target.value)} - className="w-full px-2 py-1 rounded-md bg-[#161617] text-sm" - /> -
- -
+ ) ) : (
-
+
- + {type.toLowerCase()}
+
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`} >
+ = scrollP + 80 @@ -196,7 +197,7 @@ export function NewNavbar({ // title={sessions ? "Go to Profile" : "Login With AniList"} > */} {session ? ( -
+
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: '', width: 300, @@ -261,7 +261,7 @@ export default function NewPlayer({ index: 11, position: "right", tooltip: "Theater (t)", - html: '

', + html: '', 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
; 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 }} >
+ {!data?.error && !url && ( +
+ +
+ )} {!error ? ( - !loading && - track && - url && ( + !loading && track && url && !data?.error ? ( + ) : ( +

+ {data?.status === 404 && "Not Found"} +
+ {data?.error} +

) ) : (

diff --git a/components/watch/player/utils/getZoroSource.js b/components/watch/player/utils/getZoroSource.js deleted file mode 100644 index e69de29..0000000 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 (

-

Up Next

+
+ + {episode && ( +
+ + +
+ )} +
{episode && episode.length > 0 ? ( map?.some( -- cgit v1.2.3