aboutsummaryrefslogtreecommitdiff
path: root/components/anime
diff options
context:
space:
mode:
Diffstat (limited to 'components/anime')
-rw-r--r--components/anime/charactersCard.js160
-rw-r--r--components/anime/episode.js114
-rw-r--r--components/anime/infoDetails.js204
-rw-r--r--components/anime/mobile/topSection.js179
-rw-r--r--components/anime/viewMode/thumbnailDetail.js10
-rw-r--r--components/anime/viewMode/thumbnailOnly.js4
-rw-r--r--components/anime/viewSelector.js (renamed from components/anime/changeView.js)6
-rw-r--r--components/anime/watch/primary/details.js185
-rw-r--r--components/anime/watch/primarySide.js276
-rw-r--r--components/anime/watch/secondarySide.js134
10 files changed, 198 insertions, 1074 deletions
diff --git a/components/anime/charactersCard.js b/components/anime/charactersCard.js
index abff2ba..6c9197a 100644
--- a/components/anime/charactersCard.js
+++ b/components/anime/charactersCard.js
@@ -3,79 +3,91 @@ import Image from "next/image";
import { useState } from "react";
export default function Characters({ info }) {
+ const [showAll, setShowAll] = useState(false);
- const [showAll, setShowAll] = useState(false);
-
- return (
- <div>
- <div className="flex items-center justify-between lg:gap-3 px-5 z-40 ">
- <h1 className="font-karla text-[20px] font-bold">Characters</h1>
- {info?.length > 6 && (
- <div className="cursor-pointer font-karla" onClick={() => setShowAll(!showAll)}>
- {showAll ? "show less" : "show more"}
- </div>
- )}
- </div>
- {/* for bigger device */}
- <div className="hidden md:grid w-full grid-cols-1 gap-[10px] md:gap-4 md:grid-cols-3 md:pt-7 md:pb-5 px-3 md:px-5 pt-4">
- {info.slice(0, showAll ? info.length : 6).map((item, index) => {
- return <a key={index} className="md:hover:scale-[1.02] snap-start hover:shadow-lg scale-100 transition-transform duration-200 ease-out w-full cursor-default">
- <div className="text-gray-300 space-x-4 col-span-1 flex w-full h-24 bg-secondary rounded-md overflow-hidden">
- <div className="relative h-full w-20">
- <Image
- draggable={false}
- src={
- item.node.image.large ||
- item.node.image.medium
- }
- width={500}
- height={300}
- alt={
- item.node.name.userPreferred ||
- item.node.name.full ||
- "Character Image"
- }
- className="h-full object-cover"
- />
- </div>
- <div className="py-2 flex flex-col justify-between">
- <p className="font-semibold">{item.node.name.full || item.node.name.userPreferred}</p>
- <p>{item.role}</p>
- </div>
- </div>
- </a>
- })}
- </div>
- {/* for smaller devices */}
- <div className="flex md:hidden h-full w-full select-none overflow-x-scroll overflow-y-hidden scrollbar-hide gap-4 pt-8 pb-4 px-5 z-30">
- {info.slice(0, showAll ? info.length : 6).map((item, index) => {
- return <div key={index} className="flex flex-col gap-3 shrink-0 cursor-pointer">
- <a className="hover:scale-105 hover:shadow-lg duration-300 ease-out group relative">
- <div className="h-[190px] w-[135px] rounded-md z-30">
- <Image
- draggable={false}
- src={
- item.node.image.large ||
- item.node.image.medium
- }
- alt={
- item.node.name.userPreferred ||
- item.node.name.full ||
- "Character Image"
- }
- width={500}
- height={300}
- className="z-20 h-[190px] w-[135px] object-cover rounded-md brightness-90"
- />
- </div>
- </a>
- <a className="w-[135px] lg:w-[185px] line-clamp-2">
- <h1 className="font-karla font-semibold text-[15px]">{item.node.name.full || item.node.name.userPreferred}</h1>
- <h1 className="font-karla float-right italic text-[12px]">~{item.role}</h1>
- </a>
- </div>
- })}
+ return (
+ <div>
+ <div className="flex items-center justify-between lg:gap-3 px-5 z-40 ">
+ <h1 className="font-karla text-[20px] font-bold">Characters</h1>
+ {info?.length > 6 && (
+ <div
+ className="cursor-pointer font-karla"
+ onClick={() => setShowAll(!showAll)}
+ >
+ {showAll ? "show less" : "show more"}
+ </div>
+ )}
+ </div>
+ {/* for bigger device */}
+ <div className="hidden md:grid w-full grid-cols-1 gap-[10px] md:gap-4 md:grid-cols-3 md:pt-7 md:pb-5 px-3 md:px-5 pt-4">
+ {info.slice(0, showAll ? info.length : 6).map((item, index) => {
+ return (
+ <a
+ key={index}
+ className="md:hover:scale-[1.02] snap-start hover:shadow-lg scale-100 transition-transform duration-200 ease-out w-full cursor-default"
+ >
+ <div className="text-gray-300 space-x-4 col-span-1 flex w-full h-24 bg-secondary rounded-md overflow-hidden">
+ <div className="relative h-full w-20">
+ <Image
+ draggable={false}
+ src={item.node.image.large || item.node.image.medium}
+ width={500}
+ height={300}
+ alt={
+ item.node.name.userPreferred ||
+ item.node.name.full ||
+ "Character Image"
+ }
+ className="h-full object-cover"
+ />
+ </div>
+ <div className="py-2 flex flex-col justify-between">
+ <p className="font-semibold">
+ {item.node.name.full || item.node.name.userPreferred}
+ </p>
+ <p>{item.role}</p>
+ </div>
+ </div>
+ </a>
+ );
+ })}
+ </div>
+ {/* for smaller devices */}
+ <div className="flex md:hidden h-full w-full select-none overflow-x-scroll overflow-y-hidden scrollbar-hide gap-4 pt-8 pb-4 px-5 z-30">
+ {info.slice(0, showAll ? info.length : 6).map((item, index) => {
+ return (
+ <div
+ key={index}
+ className="flex flex-col gap-3 shrink-0 cursor-pointer"
+ >
+ <a className="hover:scale-105 hover:shadow-lg duration-300 ease-out group relative">
+ <div className="h-[190px] w-[135px] rounded-md z-30">
+ <Image
+ draggable={false}
+ src={item.node.image.large || item.node.image.medium}
+ alt={
+ item.node.name.userPreferred ||
+ item.node.name.full ||
+ "Character Image"
+ }
+ width={500}
+ height={300}
+ className="z-20 h-[190px] w-[135px] object-cover rounded-md brightness-90"
+ />
+ </div>
+ </a>
+ <a className="w-[135px] lg:w-[185px] line-clamp-2">
+ <h1 className="font-karla font-semibold text-[15px]">
+ {item.node.name.full || item.node.name.userPreferred}
+ </h1>
+ <h1 className="font-karla float-right italic text-[12px]">
+ ~{item.role}
+ </h1>
+ </a>
</div>
- </div>
- );
-} \ No newline at end of file
+ );
+ })}
+ </div>
+ </div>
+ );
+}
diff --git a/components/anime/episode.js b/components/anime/episode.js
index b2f4bd7..e6420a7 100644
--- a/components/anime/episode.js
+++ b/components/anime/episode.js
@@ -1,10 +1,10 @@
import { useEffect, useState, Fragment } from "react";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
-import ChangeView from "./changeView";
+import ViewSelector from "./viewSelector";
import ThumbnailOnly from "./viewMode/thumbnailOnly";
import ThumbnailDetail from "./viewMode/thumbnailDetail";
import ListMode from "./viewMode/listMode";
-import { convertSecondsToTime } from "../../utils/getTimes";
+import { toast } from "react-toastify";
export default function AnimeEpisode({
info,
@@ -93,8 +93,9 @@ export default function AnimeEpisode({
!mapProviders ||
mapProviders?.every(
(item) =>
+ item?.img?.includes("https://s4.anilist.co/") ||
item?.image?.includes("https://s4.anilist.co/") ||
- item?.image === null
+ item?.img === null
)
) {
setView(3);
@@ -152,27 +153,106 @@ export default function AnimeEpisode({
}
}, [providerId, artStorage, info.id, session?.user?.name]);
+ let debounceTimeout;
+
+ const handleRefresh = async () => {
+ try {
+ setLoading(true);
+ clearTimeout(debounceTimeout);
+ debounceTimeout = setTimeout(async () => {
+ const res = await fetch(
+ `/api/v2/episode/${info.id}?releasing=${
+ info.status === "RELEASING" ? "true" : "false"
+ }${isDub ? "&dub=true" : ""}&refresh=true`
+ );
+ if (!res.ok) {
+ console.log(res);
+ toast.error("Something went wrong", {
+ position: "bottom-left",
+ autoClose: 3000,
+ hideProgressBar: true,
+ theme: "colored",
+ });
+ setProviders([]);
+ setLoading(false);
+ } else {
+ const data = await res.json();
+ const getMap = data.find((i) => i?.map === true);
+ let allProvider = data;
+
+ if (getMap) {
+ allProvider = data.filter((i) => {
+ if (i?.providerId === "gogoanime" && i?.map !== true) {
+ return null;
+ }
+ return i;
+ });
+ setMapProviders(getMap?.episodes);
+ }
+
+ if (allProvider.length > 0) {
+ const defaultProvider = allProvider.find(
+ (x) => x.providerId === "gogoanime" || x.providerId === "9anime"
+ );
+ setProviderId(
+ defaultProvider?.providerId || allProvider[0].providerId
+ ); // set to first provider id
+ }
+
+ setView(Number(localStorage.getItem("view")) || 3);
+ setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings")));
+ setProviders(allProvider);
+ setLoading(false);
+ }
+ }, 1000);
+ } catch (err) {
+ console.log(err);
+ toast.error("Something went wrong", {
+ position: "bottom-left",
+ autoClose: 3000,
+ hideProgressBar: true,
+ theme: "colored",
+ });
+ }
+ };
+
return (
<>
<div className="flex flex-col gap-5 px-3">
<div className="flex lg:flex-row flex-col gap-5 lg:gap-0 justify-between ">
<div className="flex justify-between">
- <div className="flex items-center md:gap-5">
+ <div className="flex items-center gap-4 md:gap-5">
{info && (
<h1 className="text-[20px] lg:text-2xl font-bold font-karla">
Episodes
</h1>
)}
- {info.nextAiringEpisode?.timeUntilAiring && (
- <p className="hidden md:block bg-gray-100 text-gray-900 rounded-md px-2 font-karla font-medium">
- Ep {info.nextAiringEpisode.episode}{" "}
- <span className="animate-pulse">{">>"}</span>{" "}
- <span className="font-bold">
- {convertSecondsToTime(
- info.nextAiringEpisode.timeUntilAiring
- )}{" "}
+ {info?.status !== "NOT_YET_RELEASED" && (
+ <button
+ type="button"
+ onClick={() => {
+ handleRefresh();
+ setProviders(null);
+ setMapProviders(null);
+ }}
+ className="relative flex flex-col items-center w-5 h-5 group"
+ >
+ <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">
+ Refresh Episodes
</span>
- </p>
+ <svg
+ fill="currentColor"
+ viewBox="0 0 20 20"
+ xmlns="http://www.w3.org/2000/svg"
+ aria-hidden="true"
+ >
+ <path
+ clipRule="evenodd"
+ fillRule="evenodd"
+ d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
+ />
+ </svg>
+ </button>
)}
</div>
@@ -267,7 +347,7 @@ export default function AnimeEpisode({
</>
)}
- <ChangeView
+ <ViewSelector
view={view}
setView={setView}
episode={currentEpisodes}
@@ -301,7 +381,7 @@ export default function AnimeEpisode({
key={index}
index={index}
info={info}
- image={mapData?.image}
+ image={mapData?.img || mapData?.image}
providerId={providerId}
episode={episode}
artStorage={artStorage}
@@ -312,7 +392,7 @@ export default function AnimeEpisode({
{view === 2 && (
<ThumbnailDetail
key={index}
- image={mapData?.image}
+ image={mapData?.img || mapData?.image}
title={mapData?.title}
description={mapData?.description}
index={index}
@@ -346,7 +426,7 @@ export default function AnimeEpisode({
</div>
)
) : (
- <p>{providers.message}</p>
+ <p>{providers?.message}</p>
)}
</div>
) : (
diff --git a/components/anime/infoDetails.js b/components/anime/infoDetails.js
deleted file mode 100644
index 8200bfa..0000000
--- a/components/anime/infoDetails.js
+++ /dev/null
@@ -1,204 +0,0 @@
-import Image from "next/image";
-import Link from "next/link";
-import Skeleton from "react-loading-skeleton";
-
-export default function DesktopDetails({
- info,
- statuses,
- handleOpen,
- loading,
- color,
- setShowAll,
- showAll,
-}) {
- return (
- <>
- <div className="hidden lg:flex gap-8 w-full flex-nowrap">
- <div className="shrink-0 lg:h-[250px] lg:w-[180px] w-[115px] h-[164px] relative">
- {info ? (
- <>
- <div className="bg-image lg:h-[250px] lg:w-[180px] w-[115px] h-[164px] bg-opacity-30 absolute backdrop-blur-lg z-10 -top-7" />
- <Image
- src={info.coverImage.extraLarge || info.coverImage.large}
- priority={true}
- alt="poster anime"
- height={700}
- width={700}
- className="object-cover lg:h-[250px] lg:w-[180px] w-[115px] h-[164px] z-20 absolute rounded-md -top-7"
- />
- <button
- type="button"
- className="bg-action flex-center z-20 h-[20px] w-[180px] absolute bottom-0 rounded-sm font-karla font-bold"
- onClick={() => handleOpen()}
- >
- {!loading
- ? statuses
- ? statuses.name
- : "Add to List"
- : "Loading..."}
- </button>
- </>
- ) : (
- <Skeleton className="h-[250px] w-[180px]" />
- )}
- </div>
-
- <div className="hidden lg:flex w-full flex-col gap-5 h-[250px]">
- <div className="flex flex-col gap-2">
- <h1
- className="title font-inter font-bold text-[36px] text-white line-clamp-1"
- title={info?.title?.romaji || info?.title?.english}
- >
- {info ? (
- info?.title?.romaji || info?.title?.english
- ) : (
- <Skeleton width={450} />
- )}
- </h1>
- {info ? (
- <div className="flex gap-6">
- {info?.episodes && (
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- {info?.episodes} Episodes
- </div>
- )}
- {info?.startDate?.year && (
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- {info?.startDate?.year}
- </div>
- )}
- {info?.averageScore && (
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- {info?.averageScore}%
- </div>
- )}
- {info?.type && (
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- {info?.type}
- </div>
- )}
- {info?.status && (
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- {info?.status}
- </div>
- )}
- <div
- className={`dynamic-text rounded-md px-2 font-karla font-bold`}
- style={color}
- >
- Sub | EN
- </div>
- </div>
- ) : (
- <Skeleton width={240} height={32} />
- )}
- </div>
- {info ? (
- <p
- dangerouslySetInnerHTML={{ __html: info?.description }}
- className="overflow-y-scroll scrollbar-thin pr-2 scrollbar-thumb-secondary scrollbar-thumb-rounded-lg h-[140px]"
- />
- ) : (
- <Skeleton className="h-[130px]" />
- )}
- </div>
- </div>
-
- <div>
- <div className="flex gap-5 items-center">
- {info?.relations?.edges?.length > 0 && (
- <div className="p-3 lg:p-0 text-[20px] lg:text-2xl font-bold font-karla">
- Relations
- </div>
- )}
- {info?.relations?.edges?.length > 3 && (
- <div
- className="cursor-pointer"
- onClick={() => setShowAll(!showAll)}
- >
- {showAll ? "show less" : "show more"}
- </div>
- )}
- </div>
- <div
- className={`w-screen lg:w-full flex gap-5 overflow-x-scroll snap-x scroll-px-5 scrollbar-none lg:grid lg:grid-cols-3 justify-items-center lg:pt-7 lg:pb-5 px-3 lg:px-4 pt-4 rounded-xl`}
- >
- {info?.relations?.edges ? (
- info?.relations?.edges
- .slice(0, showAll ? info?.relations?.edges.length : 3)
- .map((r, index) => {
- const rel = r.node;
- return (
- <Link
- key={rel.id}
- href={
- rel.type === "ANIME" ||
- rel.type === "OVA" ||
- rel.type === "MOVIE" ||
- rel.type === "SPECIAL" ||
- rel.type === "ONA"
- ? `/en/anime/${rel.id}`
- : `/en/manga/${rel.id}`
- }
- className={`lg:hover:scale-[1.02] snap-start hover:shadow-lg scale-100 transition-transform duration-200 ease-out w-full ${
- rel.type === "MUSIC" ? "pointer-events-none" : ""
- }`}
- >
- <div
- key={rel.id}
- className="w-[400px] lg:w-full h-[126px] bg-secondary flex rounded-md"
- >
- <div className="w-[90px] bg-image rounded-l-md shrink-0">
- <Image
- src={rel.coverImage.extraLarge}
- alt={rel.id}
- height={500}
- width={500}
- className="object-cover h-full w-full shrink-0 rounded-l-md"
- />
- </div>
- <div className="h-full grid px-3 items-center">
- <div className="text-action font-outfit font-bold">
- {r.relationType}
- </div>
- <div className="font-outfit font-thin line-clamp-2">
- {rel.title.userPreferred}
- </div>
- <div className={``}>{rel.type}</div>
- </div>
- </div>
- </Link>
- );
- })
- ) : (
- <>
- {[1, 2, 3].map((item) => (
- <div key={item} className="w-full hidden lg:block">
- <Skeleton className="h-[126px]" />
- </div>
- ))}
- <div className="w-full lg:hidden">
- <Skeleton className="h-[126px]" />
- </div>
- </>
- )}
- </div>
- </div>
- </>
- );
-}
diff --git a/components/anime/mobile/topSection.js b/components/anime/mobile/topSection.js
index 4420d24..8db1465 100644
--- a/components/anime/mobile/topSection.js
+++ b/components/anime/mobile/topSection.js
@@ -1,188 +1,15 @@
-import {
- ArrowUpCircleIcon,
- MagnifyingGlassIcon,
-} from "@heroicons/react/24/solid";
-
-import {
- ArrowLeftIcon,
- PlayIcon,
- PlusIcon,
- ShareIcon,
- UserIcon,
-} from "@heroicons/react/24/solid";
+import { PlayIcon, PlusIcon, ShareIcon } from "@heroicons/react/24/solid";
import Image from "next/image";
import { useRouter } from "next/router";
-import { useSearch } from "../../../lib/hooks/isOpenState";
import { useEffect, useState } from "react";
import { convertSecondsToTime } from "../../../utils/getTimes";
import Link from "next/link";
-import { signIn } from "next-auth/react";
import InfoChip from "./reused/infoChip";
import Description from "./reused/description";
-
-const getScrollPosition = (el = window) => ({
- x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
- y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop,
-});
-
-export function NewNavbar({ info, session, scrollP = 200, toTop = false }) {
- const router = useRouter();
- const [scrollPosition, setScrollPosition] = useState();
- const { isOpen, setIsOpen } = useSearch();
-
- useEffect(() => {
- const handleScroll = () => {
- setScrollPosition(getScrollPosition());
- };
-
- // Add a scroll event listener when the component mounts
- window.addEventListener("scroll", handleScroll);
-
- // Clean up the event listener when the component unmounts
- return () => {
- window.removeEventListener("scroll", handleScroll);
- };
- }, []);
- return (
- <>
- <nav
- className={`fixed z-[200] top-0 py-3 px-5 w-full ${
- scrollPosition?.y >= scrollP
- ? "bg-tersier shadow-tersier shadow-sm"
- : ""
- } transition-all duration-200 ease-linear`}
- >
- <div className="flex items-center justify-between max-w-screen-2xl mx-auto">
- <div className="flex w-full items-center gap-4">
- {info ? (
- <>
- <button
- type="button"
- className="flex-center w-7 h-7 text-white"
- onClick={() => {
- // router.back();
- router.push("/en");
- }}
- >
- <ArrowLeftIcon className="w-full h-full" />
- </button>
- <span
- className={`font-inter font-semibold w-[50%] line-clamp-1 select-none ${
- scrollPosition?.y >= scrollP + 80
- ? "opacity-100"
- : "opacity-0"
- } transition-all duration-200 ease-linear`}
- >
- {info.title.romaji}
- </span>
- </>
- ) : (
- // <></>
- <Link
- href={"/en"}
- className="flex-center text-white font-outfit text-2xl font-semibold"
- >
- moopa
- </Link>
- )}
- </div>
- <div className="flex items-center gap-4">
- <button
- type="button"
- onClick={() => setIsOpen(true)}
- className="flex-center w-[26px] h-[26px]"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="32"
- height="32"
- viewBox="0 0 24 24"
- >
- <path
- fill="none"
- stroke="currentColor"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- d="M15 15l6 6m-11-4a7 7 0 110-14 7 7 0 010 14z"
- ></path>
- </svg>
- </button>
- {/* <div
- className="bg-white"
- // title={sessions ? "Go to Profile" : "Login With AniList"}
- > */}
- {session ? (
- <div className="w-7 h-7 relative flex flex-col items-center group">
- <button
- type="button"
- onClick={() =>
- router.push(`/en/profile/${session?.user.name}`)
- }
- className="rounded-full bg-white/30 overflow-hidden"
- >
- <Image
- src={session?.user.image.large}
- alt="avatar"
- width={50}
- height={50}
- className="w-full h-full object-cover"
- />
- </button>
- <div className="hidden absolute z-50 w-28 text-center -bottom-20 text-white shadow-2xl opacity-0 bg-secondary p-1 py-2 rounded-md font-karla font-light invisible group-hover:visible group-hover:opacity-100 duration-300 transition-all md:grid place-items-center gap-1">
- <Link
- href={`/en/profile/${session?.user.name}`}
- className="hover:text-action"
- >
- Profile
- </Link>
- <div
- onClick={() => signOut("AniListProvider")}
- className="hover:text-action"
- >
- Log out
- </div>
- </div>
- </div>
- ) : (
- <button
- type="button"
- onClick={() => signIn("AniListProvider")}
- title="Login With AniList"
- className="w-7 h-7 bg-white/30 rounded-full overflow-hidden"
- >
- <UserIcon className="w-full h-full translate-y-2" />
- </button>
- )}
- {/* </div> */}
- </div>
- </div>
- </nav>
- {toTop && (
- <button
- type="button"
- onClick={() => {
- window.scrollTo({
- top: 0,
- behavior: "smooth",
- });
- }}
- className={`${
- scrollPosition?.y >= 180
- ? "-translate-x-6 opacity-100"
- : "translate-x-[100%] opacity-0"
- } transform transition-all duration-300 ease-in-out fixed bottom-24 lg:bottom-14 right-0 z-[500]`}
- >
- <ArrowUpCircleIcon className="w-10 h-10 text-white" />
- </button>
- )}
- </>
- );
-}
+import { NewNavbar } from "@/components/shared/NavBar";
export default function DetailTop({
info,
- session,
statuses,
handleOpen,
watchUrl,
@@ -217,7 +44,7 @@ export default function DetailTop({
return (
<div className="gap-6 w-full px-3 pt-4 md:pt-10 flex flex-col items-center justify-center">
- <NewNavbar info={info} session={session} />
+ <NewNavbar info={info} />
{/* MAIN */}
<div className="flex flex-col md:flex-row w-full items-center md:items-end gap-5 pt-12">
diff --git a/components/anime/viewMode/thumbnailDetail.js b/components/anime/viewMode/thumbnailDetail.js
index db18651..2abfd0b 100644
--- a/components/anime/viewMode/thumbnailDetail.js
+++ b/components/anime/viewMode/thumbnailDetail.js
@@ -32,8 +32,8 @@ export default function ThumbnailDetail({
<Image
src={image || ""}
alt={`Episode ${epi?.number} Thumbnail`}
- width={1000}
- height={1000}
+ width={420}
+ height={236}
className="object-cover z-30 rounded-lg h-[110px] lg:h-[160px] brightness-[65%]"
/>
)}
@@ -41,7 +41,7 @@ export default function ThumbnailDetail({
className={`absolute bottom-0 left-0 h-[2px] bg-red-700`}
style={{
width:
- progress && artStorage && epi?.number <= progress
+ progress || (artStorage && epi?.number <= progress)
? "100%"
: artStorage?.[epi?.id]
? `${prog}%`
@@ -49,7 +49,7 @@ export default function ThumbnailDetail({
}}
/>
<span className="absolute bottom-2 left-2 font-karla font-semibold text-sm lg:text-lg">
- Episode {epi?.number}
+ Episode {epi?.number || 0}
</span>
<div className="z-[9999] absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 scale-[1.5]">
<svg
@@ -68,7 +68,7 @@ export default function ThumbnailDetail({
className={`w-[70%] h-full select-none p-4 flex flex-col justify-center gap-3`}
>
<h1 className="font-karla font-bold text-base lg:text-lg xl:text-xl italic line-clamp-1">
- {title || `Episode ${epi?.number}`}
+ {title || `Episode ${epi?.number || 0}`}
</h1>
{description && (
<p className="line-clamp-2 text-xs lg:text-md xl:text-lg italic font-outfit font-extralight">
diff --git a/components/anime/viewMode/thumbnailOnly.js b/components/anime/viewMode/thumbnailOnly.js
index 69cd8c3..7259beb 100644
--- a/components/anime/viewMode/thumbnailOnly.js
+++ b/components/anime/viewMode/thumbnailOnly.js
@@ -23,7 +23,7 @@ export default function ThumbnailOnly({
className="transition-all duration-200 ease-out lg:hover:scale-105 hover:ring-1 hover:ring-white cursor-pointer bg-secondary shrink-0 relative w-full h-[180px] sm:h-[130px] subpixel-antialiased rounded-md overflow-hidden"
>
<span className="absolute text-sm z-40 bottom-1 left-2 font-karla font-semibold text-white">
- Episode {episode?.number}
+ Episode {episode?.number || 0}
</span>
<span
className={`absolute bottom-7 left-0 h-[2px] bg-red-600`}
@@ -40,7 +40,7 @@ export default function ThumbnailOnly({
{image && (
<Image
src={image || ""}
- alt="epi image"
+ alt={`Episode ${episode?.number} Thumbnail`}
width={500}
height={500}
className="object-cover w-full h-[150px] sm:h-[100px] z-20 brightness-75"
diff --git a/components/anime/changeView.js b/components/anime/viewSelector.js
index 75ebdff..f114a8b 100644
--- a/components/anime/changeView.js
+++ b/components/anime/viewSelector.js
@@ -1,4 +1,4 @@
-export default function ChangeView({ view, setView, episode, map }) {
+export default function ViewSelector({ view, setView, episode, map }) {
return (
<div className="flex gap-3 rounded-sm items-center p-2">
<div
@@ -6,6 +6,7 @@ export default function ChangeView({ view, setView, episode, map }) {
episode?.length > 0
? map?.every(
(item) =>
+ item?.img?.includes("https://s4.anilist.co/") ||
item?.image?.includes("https://s4.anilist.co/") ||
item.title === null
) || !map
@@ -32,6 +33,7 @@ export default function ChangeView({ view, setView, episode, map }) {
episode?.length > 0
? map?.every(
(item) =>
+ item?.img?.includes("https://s4.anilist.co/") ||
item?.image?.includes("https://s4.anilist.co/") ||
item.title === null
) || !map
@@ -50,6 +52,7 @@ export default function ChangeView({ view, setView, episode, map }) {
episode?.length > 0
? map?.every(
(item) =>
+ item?.img?.includes("https://s4.anilist.co/") ||
item?.image?.includes("https://s4.anilist.co/") ||
item.title === null
) || !map
@@ -71,6 +74,7 @@ export default function ChangeView({ view, setView, episode, map }) {
episode?.length > 0
? map?.every(
(item) =>
+ item?.img?.includes("https://s4.anilist.co/") ||
item?.image?.includes("https://s4.anilist.co/") ||
item.title === null
) || !map
diff --git a/components/anime/watch/primary/details.js b/components/anime/watch/primary/details.js
deleted file mode 100644
index f092879..0000000
--- a/components/anime/watch/primary/details.js
+++ /dev/null
@@ -1,185 +0,0 @@
-import { useEffect, useState } from "react";
-import { useAniList } from "../../../../lib/anilist/useAnilist";
-import Skeleton from "react-loading-skeleton";
-import DisqusComments from "../../../disqus";
-import Image from "next/image";
-
-export default function Details({
- info,
- session,
- epiNumber,
- description,
- id,
- onList,
- setOnList,
- handleOpen,
- disqus,
-}) {
- const [showComments, setShowComments] = useState(false);
- const { markPlanning } = useAniList(session);
- const [url, setUrl] = useState(null);
-
- function handlePlan() {
- if (onList === false) {
- markPlanning(info.id);
- setOnList(true);
- }
- }
-
- useEffect(() => {
- const url = window.location.href;
- setShowComments(false);
- setUrl(url);
- }, [id]);
-
- return (
- <div className="flex flex-col gap-2">
- <div className="px-4 pt-7 pb-4 h-full flex">
- <div className="aspect-[9/13] h-[240px]">
- {info ? (
- <Image
- src={info.coverImage.extraLarge}
- alt="Anime Cover"
- width={1000}
- height={1000}
- priority
- className="object-cover aspect-[9/13] h-[240px] rounded-md"
- />
- ) : (
- <Skeleton height={240} />
- )}
- </div>
- <div
- className="grid w-full pl-5 gap-3 h-[240px]"
- data-episode={info?.episodes || "0"}
- >
- <div className="grid grid-cols-2 gap-1 items-center">
- <h2 className="text-sm font-light font-roboto text-[#878787]">
- Studios
- </h2>
- <div className="row-start-2">
- {info ? info.studios.edges[0].node.name : <Skeleton width={80} />}
- </div>
- <div className="hidden xxs:grid col-start-2 place-content-end relative">
- <div>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- onClick={() => {
- session ? handlePlan() : handleOpen();
- }}
- className={`w-8 h-8 hover:fill-white text-white hover:cursor-pointer ${
- onList ? "fill-white" : ""
- }`}
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0111.186 0z"
- />
- </svg>
- </div>
- </div>
- </div>
- <div className="grid gap-1 items-center">
- <h2 className="text-sm font-light font-roboto text-[#878787]">
- Status
- </h2>
- <div>{info ? info.status : <Skeleton width={75} />}</div>
- </div>
- <div className="grid gap-1 items-center overflow-y-hidden">
- <h2 className="text-sm font-light font-roboto text-[#878787]">
- Titles
- </h2>
- <div className="grid grid-flow-dense grid-cols-2 gap-2 h-full w-full">
- {info ? (
- <>
- <div className="title-rm line-clamp-3">
- {info.title?.romaji || ""}
- </div>
- <div className="title-en line-clamp-3">
- {info.title?.english || ""}
- </div>
- <div className="title-nt line-clamp-3">
- {info.title?.native || ""}
- </div>
- </>
- ) : (
- <Skeleton width={200} height={50} />
- )}
- </div>
- </div>
- </div>
- </div>
- <div className="flex flex-wrap gap-3 px-4 pt-3">
- {info &&
- info.genres?.map((item, index) => (
- <div
- key={index}
- className="border border-action text-gray-100 py-1 px-2 rounded-md font-karla text-sm"
- >
- {item}
- </div>
- ))}
- </div>
- <div className={`bg-secondary rounded-md mt-3 mx-3`}>
- {info && (
- <p
- dangerouslySetInnerHTML={{ __html: description }}
- className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `}
- />
- )}
- </div>
- {/* {<div className="mt-5 px-5"></div>} */}
- {!showComments && (
- <div className="w-full flex justify-center py-2 font-karla px-3 lg:px-0">
- <button
- onClick={() => setShowComments(true)}
- className={
- showComments
- ? "hidden"
- : "flex-center gap-2 h-10 bg-secondary rounded w-full lg:w-[50%]"
- }
- >
- Load Disqus{" "}
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth="1.5"
- stroke="currentColor"
- className="w-5 h-5"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"
- />
- </svg>
- </button>
- </div>
- )}
- {showComments && (
- <div>
- {info && url && (
- <div className="mt-5 px-5">
- <DisqusComments
- key={id}
- post={{
- id: id,
- title: info.title.romaji,
- url: url,
- episode: epiNumber,
- name: disqus,
- }}
- />
- </div>
- )}
- </div>
- )}
- </div>
- );
-}
diff --git a/components/anime/watch/primarySide.js b/components/anime/watch/primarySide.js
deleted file mode 100644
index a3d9f4f..0000000
--- a/components/anime/watch/primarySide.js
+++ /dev/null
@@ -1,276 +0,0 @@
-import { useEffect, useState } from "react";
-import { ChevronDownIcon } from "@heroicons/react/20/solid";
-import { ForwardIcon } from "@heroicons/react/24/solid";
-import { useRouter } from "next/router";
-import { signIn } from "next-auth/react";
-import Details from "./primary/details";
-import VideoPlayer from "../../videoPlayer";
-import Link from "next/link";
-import Skeleton from "react-loading-skeleton";
-import Modal from "../../modal";
-import AniList from "../../media/aniList";
-
-export default function PrimarySide({
- info,
- session,
- epiNumber,
- navigation,
- providerId,
- watchId,
- onList,
- proxy,
- disqus,
- setOnList,
- episodeList,
- timeWatched,
- dub,
-}) {
- const [episodeData, setEpisodeData] = useState();
- const [open, setOpen] = useState(false);
- const [skip, setSkip] = useState();
-
- const [loading, setLoading] = useState(true);
-
- const router = useRouter();
-
- useEffect(() => {
- setLoading(true);
- async function fetchData() {
- if (info) {
- const anify = await fetch("/api/v2/source", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- source:
- providerId === "gogoanime" && !watchId.startsWith("/")
- ? "consumet"
- : "anify",
- providerId: providerId,
- watchId: watchId,
- episode: epiNumber,
- id: info.id,
- sub: dub ? "dub" : "sub",
- }),
- }).then((res) => res.json());
-
- const skip = await fetch(
- `https://api.aniskip.com/v2/skip-times/${info.idMal}/${parseInt(
- epiNumber
- )}?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=`
- ).then((res) => {
- if (!res.ok) {
- switch (res.status) {
- case 404: {
- return null;
- }
- }
- }
- return res.json();
- });
-
- const op =
- skip?.results?.find((item) => item.skipType === "op") || null;
- const ed =
- skip?.results?.find((item) => item.skipType === "ed") || null;
-
- setSkip({ op, ed });
-
- setEpisodeData(anify);
- setLoading(false);
- }
- }
-
- fetchData();
- return () => {
- setEpisodeData();
- setSkip();
- };
- }, [providerId, watchId, info]);
-
- useEffect(() => {
- const mediaSession = navigator.mediaSession;
- if (!mediaSession) return;
-
- const now = navigation?.playing;
- const poster = now?.image || info?.bannerImage;
- const title = now?.title || info?.title?.romaji;
-
- const artwork = poster
- ? [{ src: poster, sizes: "512x512", type: "image/jpeg" }]
- : undefined;
-
- mediaSession.metadata = new MediaMetadata({
- title: title,
- artist: `Moopa ${
- title === info?.title?.romaji
- ? "- Episode " + epiNumber
- : `- ${info?.title?.romaji || info?.title?.english}`
- }`,
- artwork,
- });
- }, [navigation, info, epiNumber]);
-
- function handleOpen() {
- setOpen(true);
- document.body.style.overflow = "hidden";
- }
-
- function handleClose() {
- setOpen(false);
- document.body.style.overflow = "auto";
- }
-
- return (
- <>
- <Modal open={open} onClose={() => handleClose()}>
- {!session && (
- <div className="flex-center flex-col gap-5 px-10 py-5 bg-secondary rounded-md">
- <h1 className="text-md font-extrabold font-karla">
- Edit your list
- </h1>
- <button
- className="flex items-center bg-[#363642] rounded-md text-white p-1"
- onClick={() => signIn("AniListProvider")}
- >
- <h1 className="px-1 font-bold font-karla">Login with AniList</h1>
- <div className="scale-[60%] pb-[1px]">
- <AniList />
- </div>
- </button>
- </div>
- )}
- </Modal>
- <div className="w-full h-full">
- <div key={watchId} className="w-full aspect-video bg-black">
- {!loading ? (
- navigation && episodeData?.sources?.length !== 0 ? (
- <VideoPlayer
- session={session}
- info={info}
- data={episodeData}
- provider={providerId}
- id={watchId}
- progress={epiNumber}
- skip={skip}
- proxy={proxy}
- aniId={info.id}
- aniTitle={info.title?.romaji || info.title?.english}
- track={navigation}
- timeWatched={timeWatched}
- dub={dub}
- />
- ) : (
- <p className="h-full flex-center">
- Video is not available, please try other providers
- </p>
- )
- ) : (
- <div className="flex-center aspect-video bg-black">
- <div className="lds-ellipsis">
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- </div>
- </div>
- )}
- </div>
- <div className="flex flex-col divide-y divide-white/20">
- {info && episodeList ? (
- <div className="flex items-center justify-between py-3 px-3">
- <div className="flex flex-col gap-2 w-[60%]">
- <h1 className="text-xl font-outfit font-semibold line-clamp-1">
- <Link
- href={`/en/anime/${info.id}`}
- className="hover:underline"
- title={navigation?.playing?.title || info.title?.romaji}
- >
- {navigation?.playing?.title || info.title?.romaji}
- </Link>
- </h1>
- <h3 className="text-sm font-karla font-light">
- Episode {epiNumber}
- </h3>
- </div>
- <div className="flex gap-4 items-center justify-end">
- <div className="relative">
- <select
- className="flex items-center gap-5 rounded-[3px] bg-secondary py-1 px-3 pr-8 font-karla appearance-none cursor-pointer"
- value={epiNumber}
- onChange={(e) => {
- const selectedEpisode = episodeList.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}` : ""
- }`
- );
- }}
- >
- {episodeList.map((episode) => (
- <option key={episode.number} value={episode.number}>
- Episode {episode.number}
- </option>
- ))}
- </select>
- <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" />
- </div>
- <button
- disabled={!navigation?.next}
- className={`${
- !navigation?.next ? "pointer-events-none" : ""
- }relative group`}
- onClick={() => {
- router.push(
- `/en/anime/watch/${info.id}/${providerId}?id=${
- navigation?.next.id
- }&num=${navigation?.next.number}${
- dub ? `&dub=${dub}` : ""
- }`
- );
- }}
- >
- <span className="absolute z-[9999] -left-11 -top-14 p-2 shadow-xl rounded-md transform transition-all whitespace-nowrap bg-secondary lg:group-hover:block group-hover:opacity-1 hidden font-karla font-bold">
- Next Episode
- </span>
- <ForwardIcon
- className={`w-6 h-6 ${
- !navigation?.next ? "text-[#282828]" : ""
- }`}
- />
- </button>
- </div>
- </div>
- ) : (
- <div className="py-3 px-4">
- <div className="text-xl font-outfit font-semibold line-clamp-2">
- <div className="inline hover:underline">
- <Skeleton width={240} />
- </div>
- </div>
- <h4 className="text-sm font-karla font-light">
- <Skeleton width={75} />
- </h4>
- </div>
- )}
- <Details
- info={info}
- session={session}
- description={navigation?.playing?.description || info?.description}
- epiNumber={epiNumber}
- id={watchId}
- onList={onList}
- setOnList={setOnList}
- handleOpen={handleOpen}
- disqus={disqus}
- />
- </div>
- </div>
- </>
- );
-}
diff --git a/components/anime/watch/secondarySide.js b/components/anime/watch/secondarySide.js
deleted file mode 100644
index c9ef684..0000000
--- a/components/anime/watch/secondarySide.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import Skeleton from "react-loading-skeleton";
-import Image from "next/image";
-import Link from "next/link";
-
-export default function SecondarySide({
- info,
- map,
- providerId,
- watchId,
- episode,
- artStorage,
- dub,
-}) {
- const progress = info.mediaListEntry?.progress;
- return (
- <div className="lg:w-[35%] shrink-0 w-screen">
- <h1 className="text-xl font-karla pl-4 pb-5 font-semibold">Up Next</h1>
- <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((item) => item.title && item.description) > 0 ? (
- episode.map((item) => {
- const time = artStorage?.[item.id]?.timeWatched;
- const duration = artStorage?.[item.id]?.duration;
- let prog = (time / duration) * 100;
- if (prog > 90) prog = 100;
-
- const mapData = map?.find((i) => i.number === item.number);
- return (
- <Link
- href={`/en/anime/watch/${
- info.id
- }/${providerId}?id=${encodeURIComponent(item.id)}&num=${
- item.number
- }${dub ? `&dub=${dub}` : ""}`}
- key={item.id}
- className={`bg-secondary flex w-full h-[110px] rounded-lg scale-100 transition-all duration-300 ease-out ${
- item.id == watchId
- ? "pointer-events-none ring-1 ring-action"
- : "cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white"
- }`}
- >
- <div className="w-[43%] lg:w-[40%] h-[110px] relative rounded-lg z-40 shrink-0 overflow-hidden shadow-[4px_0px_5px_0px_rgba(0,0,0,0.3)]">
- <div className="relative">
- {/* {mapData?.image && ( */}
- <Image
- src={mapData?.image || info?.coverImage?.extraLarge}
- alt="Anime Cover"
- width={1000}
- height={1000}
- className={`object-cover z-30 rounded-lg h-[110px] ${
- item.id == watchId
- ? "brightness-[30%]"
- : "brightness-75"
- }`}
- />
- {/* )} */}
- <span
- className={`absolute bottom-0 left-0 h-[2px] bg-red-700`}
- style={{
- width:
- progress && artStorage && item?.number <= progress
- ? "100%"
- : artStorage?.[item?.id]
- ? `${prog}%`
- : "0",
- }}
- />
- <span className="absolute bottom-2 left-2 font-karla font-bold text-sm">
- Episode {item?.number}
- </span>
- {item.id == watchId && (
- <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 scale-[1.5]">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 20 20"
- fill="currentColor"
- className="w-5 h-5"
- >
- <path d="M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
- </svg>
- </div>
- )}
- </div>
- </div>
- <div
- className={`w-full h-full overflow-x-hidden select-none p-4 flex flex-col gap-2 ${
- item.id == watchId ? "text-[#7a7a7a]" : ""
- }`}
- >
- <h1 className="font-karla font-bold italic line-clamp-1">
- {mapData?.title}
- </h1>
- <p className="line-clamp-2 text-xs italic font-outfit font-extralight">
- {mapData?.description}
- </p>
- </div>
- </Link>
- );
- })
- ) : (
- episode.map((item) => {
- return (
- <Link
- href={`/en/anime/watch/${
- info.id
- }/${providerId}?id=${encodeURIComponent(item.id)}&num=${
- item.number
- }${dub ? `&dub=${dub}` : ""}`}
- key={item.id}
- className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${
- item.id == watchId
- ? "pointer-events-none ring-1 ring-action text-[#5d5d5d]"
- : "cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white"
- }`}
- >
- Episode {item.number}
- </Link>
- );
- })
- )
- ) : (
- <>
- {[1].map((item) => (
- <Skeleton
- key={item}
- className="bg-secondary flex w-full h-[110px] rounded-lg scale-100 transition-all duration-300 ease-out"
- />
- ))}
- </>
- )}
- </div>
- </div>
- );
-}