aboutsummaryrefslogtreecommitdiff
path: root/components/anime/episode.js
diff options
context:
space:
mode:
Diffstat (limited to 'components/anime/episode.js')
-rw-r--r--components/anime/episode.js281
1 files changed, 281 insertions, 0 deletions
diff --git a/components/anime/episode.js b/components/anime/episode.js
new file mode 100644
index 0000000..c889c25
--- /dev/null
+++ b/components/anime/episode.js
@@ -0,0 +1,281 @@
+import { useEffect, useState, Fragment } from "react";
+import { ChevronDownIcon, ClockIcon } from "@heroicons/react/20/solid";
+import { convertSecondsToTime } from "../../utils/getTimes";
+import ChangeView from "./changeView";
+import ThumbnailOnly from "./viewMode/thumbnailOnly";
+import ThumbnailDetail from "./viewMode/thumbnailDetail";
+import ListMode from "./viewMode/listMode";
+import axios from "axios";
+
+export default function AnimeEpisode({ info, progress }) {
+ const [providerId, setProviderId] = useState(); // default provider
+ const [currentPage, setCurrentPage] = useState(1); // for pagination
+ const [visible, setVisible] = useState(false); // for mobile view
+ const itemsPerPage = 13; // choose your number of items per page
+
+ const [loading, setLoading] = useState(true);
+ const [artStorage, setArtStorage] = useState(null);
+ const [view, setView] = useState(3);
+ const [isDub, setIsDub] = useState(false);
+
+ const [providers, setProviders] = useState(null);
+
+ useEffect(() => {
+ setLoading(true);
+ setProviders(null);
+ const fetchData = async () => {
+ try {
+ const { data: firstResponse } = await axios.get(
+ `/api/consumet/episode/${info.id}${isDub === true ? "?dub=true" : ""}`
+ );
+ if (firstResponse.data.length > 0) {
+ const defaultProvider = firstResponse.data?.find(
+ (x) => x.providerId === "gogoanime"
+ );
+ setProviderId(
+ defaultProvider?.providerId || firstResponse.data[0].providerId
+ ); // set to first provider id
+ }
+
+ setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings")));
+ setProviders(firstResponse.data);
+ setLoading(false);
+ } catch (error) {
+ setLoading(false);
+ setProviders([]);
+ }
+ };
+ fetchData();
+ }, [info.id, isDub]);
+
+ const episodes =
+ providers?.find((provider) => provider.providerId === providerId)
+ ?.episodes || [];
+
+ const lastEpisodeIndex = currentPage * itemsPerPage;
+ const firstEpisodeIndex = lastEpisodeIndex - itemsPerPage;
+ const currentEpisodes = episodes.slice(firstEpisodeIndex, lastEpisodeIndex);
+ const totalPages = Math.ceil(episodes.length / itemsPerPage);
+
+ const handleChange = (event) => {
+ setProviderId(event.target.value);
+ };
+
+ const handlePageChange = (pageNumber) => {
+ setCurrentPage(pageNumber);
+ };
+
+ useEffect(() => {
+ if (episodes?.some((item) => item?.title === null)) {
+ setView(3);
+ }
+ }, [providerId, episodes]);
+
+ 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 lg:gap-10 sm:gap-7 gap-3">
+ {info && (
+ <h1 className="text-[20px] lg:text-2xl font-bold font-karla">
+ Episodes
+ </h1>
+ )}
+ {info?.nextAiringEpisode && (
+ <div className="flex items-center gap-2">
+ <div className="flex items-center gap-4 text-[10px] xxs:text-sm lg:text-base">
+ <h1>Next :</h1>
+ <div className="px-4 rounded-sm font-karla font-bold bg-white text-black">
+ {convertSecondsToTime(
+ info.nextAiringEpisode.timeUntilAiring
+ )}
+ </div>
+ </div>
+ <div className="h-6 w-6">
+ <ClockIcon />
+ </div>
+ </div>
+ )}
+ </div>
+
+ <div className="flex items-center gap-2">
+ <div
+ onClick={() => setIsDub((prev) => !prev)}
+ className="flex lg:hidden flex-col items-center relative rounded-md bg-secondary py-1.5 px-3 font-karla text-sm hover:ring-1 ring-action cursor-pointer group"
+ >
+ {isDub ? "Dub" : "Sub"}
+ <span className="absolute invisible opacity-0 group-hover:opacity-100 group-hover:scale-100 scale-0 group-hover:-translate-y-7 translate-y-0 group-hover:visible rounded-sm shadow top-0 w-28 bg-secondary text-center transition-all transform duration-200 ease-out">
+ Switch to {isDub ? "Sub" : "Dub"}
+ </span>
+ </div>
+ <div
+ className="lg:hidden bg-secondary p-1 rounded-md cursor-pointer"
+ onClick={() => setVisible(!visible)}
+ >
+ <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"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z"
+ />
+ </svg>
+ </div>
+ </div>
+ </div>
+ <div
+ className={`flex lg:flex gap-3 items-center justify-between ${
+ visible ? "" : "hidden"
+ }`}
+ >
+ {providers && (
+ <div
+ onClick={() => setIsDub((prev) => !prev)}
+ className="hidden lg:flex flex-col items-center relative rounded-[3px] bg-secondary py-1 px-3 font-karla text-sm hover:ring-1 ring-action cursor-pointer group"
+ >
+ {isDub ? "Dub" : "Sub"}
+ <span className="absolute invisible opacity-0 group-hover:opacity-100 group-hover:scale-100 scale-0 group-hover:-translate-y-7 translate-y-0 group-hover:visible rounded-sm shadow top-0 w-28 bg-secondary text-center transition-all transform duration-200 ease-out">
+ Switch to {isDub ? "Sub" : "Dub"}
+ </span>
+ </div>
+ )}
+ {providers && providers.length > 0 && (
+ <>
+ <div className="flex gap-3">
+ <div className="relative flex gap-2 items-center group">
+ <select
+ title="Providers"
+ onChange={handleChange}
+ value={providerId}
+ 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"
+ >
+ {providers.map((provider) => (
+ <option
+ key={provider.providerId}
+ value={provider.providerId}
+ >
+ {provider.providerId}
+ </option>
+ ))}
+ </select>
+ {/* <span className="absolute invisible opacity-0 group-hover:opacity-100 group-hover:scale-100 scale-0 group-hover:-translate-y-7 translate-y-0 group-hover:visible rounded-sm shadow top-0 w-32 bg-secondary text-center transition-all transform duration-200 ease-out">
+ Select Providers
+ </span> */}
+ <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" />
+ </div>
+
+ {totalPages > 1 && (
+ <div className="relative flex gap-2 items-center">
+ <select
+ title="Pages"
+ onChange={(e) =>
+ handlePageChange(Number(e.target.value))
+ }
+ 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 hover:ring-1 hover:ring-action"
+ >
+ {[...Array(totalPages)].map((_, i) => (
+ <option key={i} value={i + 1}>
+ {i + 1}
+ </option>
+ ))}
+ </select>
+ <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" />
+ </div>
+ )}
+ </div>
+ </>
+ )}
+
+ <ChangeView
+ view={view}
+ setView={setView}
+ episode={currentEpisodes}
+ />
+ </div>
+ </div>
+
+ {/* Episodes */}
+ {!loading ? (
+ <div
+ className={
+ view === 1
+ ? "grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5 lg:gap-8 place-items-center"
+ : `flex flex-col gap-3`
+ }
+ >
+ {Array.isArray(providers) ? (
+ providers.length > 0 ? (
+ currentEpisodes.map((episode, index) => {
+ return (
+ <Fragment key={index}>
+ {view === 1 && (
+ <ThumbnailOnly
+ key={index}
+ index={index}
+ info={info}
+ providerId={providerId}
+ episode={episode}
+ artStorage={artStorage}
+ progress={progress}
+ dub={isDub}
+ // image={thumbnail}
+ />
+ )}
+ {view === 2 && (
+ <ThumbnailDetail
+ key={index}
+ index={index}
+ epi={episode}
+ provider={providerId}
+ info={info}
+ artStorage={artStorage}
+ progress={progress}
+ dub={isDub}
+ />
+ )}
+ {view === 3 && (
+ <ListMode
+ key={index}
+ info={info}
+ episode={episode}
+ index={index}
+ providerId={providerId}
+ progress={progress}
+ dub={isDub}
+ />
+ )}
+ </Fragment>
+ );
+ })
+ ) : (
+ <div className="h-[20vh] lg:w-full flex-center flex-col gap-5">
+ <p className="text-center font-karla font-bold lg:text-lg">
+ Oops!<br></br> It looks like this anime is not available.
+ </p>
+ </div>
+ )
+ ) : (
+ <p>{providers.message}</p>
+ )}
+ </div>
+ ) : (
+ <div className="flex justify-center">
+ <div className="lds-ellipsis">
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ </div>
+ </div>
+ )}
+ </div>
+ </>
+ );
+}