diff options
| author | Factiven <[email protected]> | 2023-07-20 17:54:58 +0700 |
|---|---|---|
| committer | Factiven <[email protected]> | 2023-07-20 17:54:58 +0700 |
| commit | 742d807b59855ca6f9ace0da90192f8e3eed4f2c (patch) | |
| tree | 7d8f858ce76df4775d74eb4a547064bedf300bc5 | |
| parent | Update v3.6.6 (diff) | |
| download | moopa-742d807b59855ca6f9ace0da90192f8e3eed4f2c.tar.xz moopa-742d807b59855ca6f9ace0da90192f8e3eed4f2c.zip | |
Update v3.6.7
> Fixed Schedule
| -rw-r--r-- | components/home/schedule.js | 167 | ||||
| -rw-r--r-- | pages/en/anime/[...id].js | 1247 | ||||
| -rw-r--r-- | pages/en/anime/watch/[...info].js | 711 | ||||
| -rw-r--r-- | pages/en/search/[param].js | 24 | ||||
| -rw-r--r-- | pages/id/anime/[...id].js | 804 | ||||
| -rw-r--r-- | pages/id/anime/watch/[...info].js | 469 | ||||
| -rw-r--r-- | pages/id/search/[param].js | 24 |
7 files changed, 1695 insertions, 1751 deletions
diff --git a/components/home/schedule.js b/components/home/schedule.js index f5aa44f..4a85143 100644 --- a/components/home/schedule.js +++ b/components/home/schedule.js @@ -12,50 +12,30 @@ export default function Schedule({ data, scheduleData, time }) { "Schedule"; currentDay = currentDay.replace("Schedule", ""); - const [activeSection, setActiveSection] = useState(currentDay); - - const scrollRef = useRef(null); + const [currentPage, setCurrentPage] = useState(0); + const [days, setDay] = useState(); useEffect(() => { if (scheduleData) { - const index = Object?.keys(scheduleData).indexOf( - activeSection + "Schedule" - ); - if (scrollRef.current) { - scrollRef.current.scrollLeft = scrollRef.current.clientWidth * index; - } + const days = Object.keys(scheduleData); + setDay(days); } - }, [activeSection, scheduleData]); + }, [scheduleData]); - const handleScroll = (e) => { - const { scrollLeft, clientWidth } = e.target; - const index = Math.floor(scrollLeft / clientWidth); - let day = Object?.keys(scheduleData)[index]; - day = day.replace("Schedule", ""); - setActiveSection(day); + const handleNextPage = () => { + setCurrentPage((prevPage) => (prevPage + 1) % days.length); }; - // buttons to scroll horizontally - const scrollLeft = () => { - if (scrollRef.current.scrollLeft === 0) { - scrollRef.current.scrollLeft = scrollRef.current.scrollWidth; - } else { - scrollRef.current.scrollLeft -= scrollRef.current.offsetWidth; - } + const handlePreviousPage = () => { + setCurrentPage((prevPage) => (prevPage - 1 + days.length) % days.length); }; - const scrollRight = () => { - const difference = - scrollRef.current.scrollWidth - - scrollRef.current.offsetWidth - - scrollRef.current.scrollLeft; - if (difference < 5) { - // adjust the threshold as needed - scrollRef.current.scrollLeft = 0; - } else { - scrollRef.current.scrollLeft += scrollRef.current.offsetWidth; - } - }; + useEffect(() => { + const todayIndex = days?.findIndex((day) => + day.toLowerCase().includes(currentDay) + ); + setCurrentPage(todayIndex >= 0 ? todayIndex : 0); + }, [currentDay, days]); return ( <div className="flex flex-col gap-5 px-4 lg:px-0"> @@ -126,84 +106,75 @@ export default function Schedule({ data, scheduleData, time }) { </div> </div> </div> - {scheduleData && ( + + {scheduleData && days && ( <div className="w-full bg-tersier rounded-b overflow-hidden"> <div - ref={scrollRef} - className="flex overflow-x-scroll snap snap-x snap-proximity scrollbar-hide" - onScroll={handleScroll} + className="snap-start flex-shrink-0 h-[240px] overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded w-full" + style={{ scrollbarGutter: "stable" }} > - {Object.entries(scheduleData).map(([section, data], index) => { - const uniqueArray = data.reduce((accumulator, current) => { - if (!accumulator.find((item) => item.id === current.id)) { - accumulator.push(current); - } - return accumulator; - }, []); - - return ( - <div - key={index} - className="snap-start flex-shrink-0 h-[240px] overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded w-full" - style={{ scrollbarGutter: "stable" }} - > - <div className="flex flex-col gap-2 px-2 pt-2"> - {uniqueArray.map((i, index) => { - const currentTime = Date.now(); - const hasAired = i.airingAt < currentTime; + <div className="flex flex-col gap-2 px-2 pt-2"> + {scheduleData[days[currentPage]] + .filter((show, index, self) => { + return index === self.findIndex((s) => s.id === show.id); + }) + .map((i, index) => { + const currentTime = Date.now(); + const hasAired = i.airingAt < currentTime; - return ( - <Link - key={`${i.id}-${index}`} - href={`/en/anime/${i.id}`} - className={`${ - hasAired ? "opacity-40" : "" - } h-full w-full flex items-center p-2 flex-shrink-0 hover:bg-secondary cursor-pointer`} - > - <div className="shrink-0"> - <Image - src={i.coverImage} - alt="coverSchedule" - width={300} - height={300} - className="w-10 h-10 object-cover rounded" - /> - </div> - <div className="flex items-center justify-between w-full"> - <div className="font-karla px-2"> - <h1 className="font-semibold text-sm line-clamp-1"> - {i.title.romaji} - </h1> - <p className="font-semibold text-xs text-gray-400"> - {convertUnixToTime(i.airingAt)} - Episode{" "} - {i.airingEpisode} - </p> - </div> - <div> - <PlayIcon className="w-6 h-6 text-gray-300" /> - </div> - </div> - </Link> - ); - })} - </div> - </div> - ); - })} + return ( + <Link + key={`${i.id}-${index}`} + href={`/en/anime/${i.id}`} + className={`${ + hasAired ? "opacity-40" : "" + } h-full w-full flex items-center p-2 flex-shrink-0 hover:bg-secondary cursor-pointer`} + > + <div className="shrink-0"> + {i.coverImage && ( + <Image + src={i.coverImage} + alt={`${i.title.romaji} cover`} + width={300} + height={300} + className="w-10 h-10 object-cover rounded" + /> + )} + </div> + <div className="flex items-center justify-between w-full"> + <div className="font-karla px-2"> + <h1 className="font-semibold text-sm line-clamp-1"> + {i.title.romaji} + </h1> + <p className="font-semibold text-xs text-gray-400"> + {convertUnixToTime(i.airingAt)} - Episode{" "} + {i.airingEpisode} + </p> + </div> + <div> + <PlayIcon className="w-6 h-6 text-gray-300" /> + </div> + </div> + </Link> + ); + })} + </div> </div> <div className="flex items-center bg-tersier justify-between font-karla p-2 border-t border-secondary/40"> <button type="button" className="bg-secondary px-2 py-1 rounded" - onClick={scrollLeft} + onClick={handlePreviousPage} > <BackwardIcon className="w-5 h-5" /> </button> - <div className="font-bold uppercase">{activeSection}</div> + <div className="font-bold uppercase"> + {days[currentPage]?.replace("Schedule", "")} + </div> <button type="button" className="bg-secondary px-2 py-1 rounded" - onClick={scrollRight} + onClick={handleNextPage} > <ForwardIcon className="w-5 h-5" /> </button> diff --git a/pages/en/anime/[...id].js b/pages/en/anime/[...id].js index 298058a..86396e3 100644 --- a/pages/en/anime/[...id].js +++ b/pages/en/anime/[...id].js @@ -1,5 +1,4 @@ -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; -import "react-loading-skeleton/dist/skeleton.css"; +import Skeleton from "react-loading-skeleton"; import { ChevronDownIcon, @@ -352,123 +351,60 @@ export default function Info({ info, color, api }) { )} </div> </Modal> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - <Layout navTop="text-white bg-primary lg:pt-0 lg:px-0 bg-slate bg-opacity-40 z-50"> - <div className="w-screen min-h-screen relative flex flex-col items-center bg-primary gap-5"> - <div className="bg-image w-screen"> - <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[300px] w-screen z-10 inset-0" /> - {info ? ( - <Image - src={ - info?.bannerImage || - info?.coverImage?.extraLarge || - info?.coverImage.large - } - priority={true} - alt="banner anime" - height={1000} - width={1000} - className="object-cover bg-image w-screen absolute top-0 left-0 h-[300px] brightness-[70%] z-0" - /> - ) : ( - <div className="bg-image w-screen absolute top-0 left-0 h-[300px]" /> - )} - </div> - <div className="lg:w-[90%] xl:w-[75%] lg:pt-[10rem] z-30 flex flex-col gap-5"> - {/* Mobile */} + <Layout navTop="text-white bg-primary lg:pt-0 lg:px-0 bg-slate bg-opacity-40 z-50"> + <div className="w-screen min-h-screen relative flex flex-col items-center bg-primary gap-5"> + <div className="bg-image w-screen"> + <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[300px] w-screen z-10 inset-0" /> + {info ? ( + <Image + src={ + info?.bannerImage || + info?.coverImage?.extraLarge || + info?.coverImage.large + } + priority={true} + alt="banner anime" + height={1000} + width={1000} + className="object-cover bg-image w-screen absolute top-0 left-0 h-[300px] brightness-[70%] z-0" + /> + ) : ( + <div className="bg-image w-screen absolute top-0 left-0 h-[300px]" /> + )} + </div> + <div className="lg:w-[90%] xl:w-[75%] lg:pt-[10rem] z-30 flex flex-col gap-5"> + {/* Mobile */} - <div className="lg:hidden pt-5 w-screen px-5 flex flex-col"> - <div className="h-[250px] flex flex-col gap-1 justify-center"> - <h1 className="font-karla font-extrabold text-lg line-clamp-1 w-[70%]"> - {info?.title?.romaji || info?.title?.english} - </h1> - <p - className="line-clamp-2 text-sm font-light antialiased w-[56%]" - dangerouslySetInnerHTML={{ __html: info?.description }} - /> - <div className="font-light flex gap-1 py-1 flex-wrap font-outfit text-[10px] text-[#ffffff] w-[70%]"> - {info?.genres - ?.slice( - 0, - info?.genres?.length > 3 ? info?.genres?.length : 3 - ) - .map((item, index) => ( - <span - key={index} - className="px-2 py-1 bg-secondary shadow-lg font-outfit font-light rounded-full" - > - <span className="">{item}</span> - </span> - ))} - </div> - {info && ( - <div className="flex items-center gap-5 pt-3 text-center"> - <div className="flex items-center gap-2 text-center"> - <button - type="button" - className="bg-action px-10 rounded-sm font-karla font-bold" - onClick={() => handleOpen()} - > - {!loading - ? statuses - ? statuses.name - : "Add to List" - : "Loading..."} - </button> - <div className="h-6 w-6"> - <HeartIcon /> - </div> - </div> - </div> - )} - </div> - <div className="bg-secondary rounded-sm xs:h-[30px]"> - <div className="grid grid-cols-3 place-content-center xxs:flex items-center justify-center h-full xxs:gap-10 p-2 text-sm"> - {info && info.status !== "NOT_YET_RELEASED" ? ( - <> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <TvIcon className="w-5 h-5 text-action" /> - <h4 className="font-karla">{info?.type}</h4> - </div> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <ArrowTrendingUpIcon className="w-5 h-5 text-action" /> - <h4>{info?.averageScore}%</h4> - </div> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <RectangleStackIcon className="w-5 h-5 text-action" /> - {info?.episodes ? ( - <h1>{info?.episodes} Episodes</h1> - ) : ( - <h1>TBA</h1> - )} - </div> - </> - ) : ( - <div>{info && "Not Yet Released"}</div> - )} - </div> + <div className="lg:hidden pt-5 w-screen px-5 flex flex-col"> + <div className="h-[250px] flex flex-col gap-1 justify-center"> + <h1 className="font-karla font-extrabold text-lg line-clamp-1 w-[70%]"> + {info?.title?.romaji || info?.title?.english} + </h1> + <p + className="line-clamp-2 text-sm font-light antialiased w-[56%]" + dangerouslySetInnerHTML={{ __html: info?.description }} + /> + <div className="font-light flex gap-1 py-1 flex-wrap font-outfit text-[10px] text-[#ffffff] w-[70%]"> + {info?.genres + ?.slice( + 0, + info?.genres?.length > 3 ? info?.genres?.length : 3 + ) + .map((item, index) => ( + <span + key={index} + className="px-2 py-1 bg-secondary shadow-lg font-outfit font-light rounded-full" + > + <span className="">{item}</span> + </span> + ))} </div> - </div> - - {/* PC */} - <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" - /> + {info && ( + <div className="flex items-center gap-5 pt-3 text-center"> + <div className="flex items-center gap-2 text-center"> <button type="button" - className="bg-action flex-center z-20 h-[20px] w-[180px] absolute bottom-0 rounded-sm font-karla font-bold" + className="bg-action px-10 rounded-sm font-karla font-bold" onClick={() => handleOpen()} > {!loading @@ -477,450 +413,461 @@ export default function Info({ info, color, api }) { : "Add to List" : "Loading..."} </button> + <div className="h-6 w-6"> + <HeartIcon /> + </div> + </div> + </div> + )} + </div> + <div className="bg-secondary rounded-sm xs:h-[30px]"> + <div className="grid grid-cols-3 place-content-center xxs:flex items-center justify-center h-full xxs:gap-10 p-2 text-sm"> + {info && info.status !== "NOT_YET_RELEASED" ? ( + <> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <TvIcon className="w-5 h-5 text-action" /> + <h4 className="font-karla">{info?.type}</h4> + </div> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <ArrowTrendingUpIcon className="w-5 h-5 text-action" /> + <h4>{info?.averageScore}%</h4> + </div> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <RectangleStackIcon className="w-5 h-5 text-action" /> + {info?.episodes ? ( + <h1>{info?.episodes} Episodes</h1> + ) : ( + <h1>TBA</h1> + )} + </div> </> ) : ( - <Skeleton className="h-[250px] w-[180px]" /> + <div>{info && "Not Yet Released"}</div> )} </div> + </div> + </div> - {/* PC */} - <div className="hidden lg:flex w-full flex-col gap-5 h-[250px]"> - <div className="flex flex-col gap-2"> - <h1 className=" font-inter font-bold text-[36px] text-white line-clamp-1"> - {info ? ( - info?.title?.romaji || info?.title?.english - ) : ( - <Skeleton width={450} /> - )} - </h1> + {/* PC */} + <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> + + {/* PC */} + <div className="hidden lg:flex w-full flex-col gap-5 h-[250px]"> + <div className="flex flex-col gap-2"> + <h1 className=" font-inter font-bold text-[36px] text-white line-clamp-1"> {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> - )} + 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} > - Sub | EN + {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> - ) : ( - <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]" - /> + </div> ) : ( - <Skeleton className="h-[130px]" /> + <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 + <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} - 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" : "" - }`} + className="w-[400px] lg:w-full h-[126px] bg-secondary flex rounded-md" > - <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 || - rel.coverImage.large - } - alt={rel.id} - height={500} - width={500} - className="object-cover h-full w-full shrink-0 rounded-l-md" - /> + <div className="w-[90px] bg-image rounded-l-md shrink-0"> + <Image + src={ + rel.coverImage.extraLarge || + rel.coverImage.large + } + 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="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 || rel.title.romaji} - </div> - <div className={``}>{rel.type}</div> + <div className="font-outfit font-thin line-clamp-2"> + {rel.title.userPreferred || rel.title.romaji} </div> + <div className={``}>{rel.type}</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"> + </div> + </Link> + ); + }) + ) : ( + <> + {[1, 2, 3].map((item) => ( + <div key={item} className="w-full hidden lg:block"> <Skeleton className="h-[126px]" /> </div> - </> - )} - </div> + ))} + <div className="w-full lg:hidden"> + <Skeleton className="h-[126px]" /> + </div> + </> + )} </div> - <div className="flex flex-col gap-5 lg:gap-10 p-3 lg:p-0"> - <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"> - {time} - </div> - </div> - <div className="h-6 w-6"> - <ClockIcon /> + </div> + <div className="flex flex-col gap-5 lg:gap-10 p-3 lg:p-0"> + <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"> + {time} </div> </div> - )} - </div> + <div className="h-6 w-6"> + <ClockIcon /> + </div> + </div> + )} + </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 + className={`flex lg:flex items-center gap-0 lg:gap-5 justify-between ${ + visible ? "" : "hidden" + }`} + > + <div className="flex items-end gap-3"> + {filterProviders?.length > 0 && ( + <div className="relative flex gap-2 items-center"> + <p className="hidden md:block">Provider</p> + <select + onChange={handleProvider} + value={prvValue} + 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" + > + {availableProviders + ?.filter((p) => p.available === true) + .map((p) => { + return ( + <option key={p.name} value={p.name}> + {p.name} + </option> + ); + })} + </select> + <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> + </div> + )} + {episode?.length > 50 && ( + <div className="relative flex gap-2 items-center"> + <p className="hidden md:block">Episodes</p> + <select + onChange={onEpisodeIndexChange} + value={selectedRange} + 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 scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded-lg" + > + <option value="All">All</option> + {[...Array(Math.ceil(episode?.length / 50))].map( + (_, index) => { + const start = index * 50 + 1; + const end = Math.min( + start + 50 - 1, + episode?.length + ); + const optionLabel = `${start} to ${end}`; + if (episode[0]?.number !== 1) { + var valueLabel = `${episode.length - end + 1}-${ + episode.length - start + 1 + }`; + } else { + var valueLabel = `${start}-${end}`; + } + return ( + <option key={valueLabel} value={valueLabel}> + {optionLabel} + </option> + ); + } + )} + </select> + <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> + </div> + )} + </div> + <div className="flex gap-3 rounded-sm items-center p-2"> <div - className="lg:hidden bg-secondary p-1 rounded-md cursor-pointer" - onClick={() => setVisible(!visible)} + className={ + episode?.length > 0 + ? episode?.some((item) => item?.title === null) + ? "pointer-events-none" + : "cursor-pointer" + : "pointer-events-none" + } + onClick={() => { + setEpiView("1"); + localStorage.setItem("epiView", "1"); + }} > <svg xmlns="http://www.w3.org/2000/svg" + width="31" + height="20" fill="none" - viewBox="0 0 24 24" - strokeWidth={1.5} - stroke="currentColor" - className="w-6 h-6" + viewBox="0 0 31 20" > - <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 - className={`flex lg:flex items-center gap-0 lg:gap-5 justify-between ${ - visible ? "" : "hidden" - }`} - > - <div className="flex items-end gap-3"> - {filterProviders?.length > 0 && ( - <div className="relative flex gap-2 items-center"> - <p className="hidden md:block">Provider</p> - <select - onChange={handleProvider} - value={prvValue} - 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" - > - {availableProviders - ?.filter((p) => p.available === true) - .map((p) => { - return ( - <option key={p.name} value={p.name}> - {p.name} - </option> - ); - })} - </select> - <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> - </div> - )} - {episode?.length > 50 && ( - <div className="relative flex gap-2 items-center"> - <p className="hidden md:block">Episodes</p> - <select - onChange={onEpisodeIndexChange} - value={selectedRange} - 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 scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded-lg" - > - <option value="All">All</option> - {[...Array(Math.ceil(episode?.length / 50))].map( - (_, index) => { - const start = index * 50 + 1; - const end = Math.min( - start + 50 - 1, - episode?.length - ); - const optionLabel = `${start} to ${end}`; - if (episode[0]?.number !== 1) { - var valueLabel = `${ - episode.length - end + 1 - }-${episode.length - start + 1}`; - } else { - var valueLabel = `${start}-${end}`; - } - return ( - <option key={valueLabel} value={valueLabel}> - {optionLabel} - </option> - ); - } - )} - </select> - <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> - </div> - )} - </div> - <div className="flex gap-3 rounded-sm items-center p-2"> - <div - className={ - episode?.length > 0 - ? episode?.some((item) => item?.title === null) - ? "pointer-events-none" - : "cursor-pointer" - : "pointer-events-none" - } - onClick={() => { - setEpiView("1"); - localStorage.setItem("epiView", "1"); - }} - > - <svg - xmlns="http://www.w3.org/2000/svg" + <rect width="31" height="20" - fill="none" - viewBox="0 0 31 20" - > - <rect - width="31" - height="20" - className={`${ - episode?.length > 0 - ? episode?.some((item) => item?.title === null) - ? "fill-[#1c1c22]" - : epiView === "1" - ? "fill-action" - : "fill-[#3A3A44]" - : "fill-[#1c1c22]" - }`} - rx="3" - ></rect> - </svg> - </div> - <div - className={ - episode?.length > 0 - ? episode?.some((item) => item?.title === null) - ? "pointer-events-none" - : "cursor-pointer" - : "pointer-events-none" - } - onClick={() => { - setEpiView("2"); - localStorage.setItem("epiView", "2"); - }} - > - <svg - xmlns="http://www.w3.org/2000/svg" - width="33" - height="20" - fill="none" className={`${ episode?.length > 0 ? episode?.some((item) => item?.title === null) ? "fill-[#1c1c22]" - : epiView === "2" + : epiView === "1" ? "fill-action" : "fill-[#3A3A44]" : "fill-[#1c1c22]" }`} - viewBox="0 0 33 20" - > - <rect width="33" height="7" y="1" rx="3"></rect> - <rect width="33" height="7" y="12" rx="3"></rect> - </svg> - </div> - <div - className={ + rx="3" + ></rect> + </svg> + </div> + <div + className={ + episode?.length > 0 + ? episode?.some((item) => item?.title === null) + ? "pointer-events-none" + : "cursor-pointer" + : "pointer-events-none" + } + onClick={() => { + setEpiView("2"); + localStorage.setItem("epiView", "2"); + }} + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="33" + height="20" + fill="none" + className={`${ episode?.length > 0 - ? `cursor-pointer` - : "pointer-events-none" - } - onClick={() => { - setEpiView("3"); - localStorage.setItem("epiView", "3"); - }} + ? episode?.some((item) => item?.title === null) + ? "fill-[#1c1c22]" + : epiView === "2" + ? "fill-action" + : "fill-[#3A3A44]" + : "fill-[#1c1c22]" + }`} + viewBox="0 0 33 20" > - <svg - xmlns="http://www.w3.org/2000/svg" - width="33" - height="20" - fill="none" - className={`${ - episode?.length > 0 - ? epiView === "3" - ? "fill-action" - : "fill-[#3A3A44]" - : "fill-[#1c1c22]" - }`} - viewBox="0 0 33 20" - > - <rect width="29" height="4" x="2" y="2" rx="2"></rect> - <rect width="29" height="4" x="2" y="8" rx="2"></rect> - <rect - width="16" - height="4" - x="2" - y="14" - rx="2" - ></rect> - </svg> - </div> + <rect width="33" height="7" y="1" rx="3"></rect> + <rect width="33" height="7" y="12" rx="3"></rect> + </svg> </div> - </div> - </div> - {!loading ? ( - Array.isArray(episode) ? ( - episode && ( - <div + <div + className={ + episode?.length > 0 + ? `cursor-pointer` + : "pointer-events-none" + } + onClick={() => { + setEpiView("3"); + localStorage.setItem("epiView", "3"); + }} + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="33" + height="20" + fill="none" className={`${ - epiView === "3" && - "scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full overflow-y-scroll hover:scrollbar-thumb-[#2e2f37] h-[640px]" + episode?.length > 0 + ? epiView === "3" + ? "fill-action" + : "fill-[#3A3A44]" + : "fill-[#1c1c22]" }`} + viewBox="0 0 33 20" > - {episode?.length !== 0 && episode ? ( - <div - className={`grid ${ - epiView === "1" - ? "grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5 lg:gap-8" - : "flex flex-col gap-5" - } pb-5 pt-2 lg:pt-0 ${ - epiView === "3" ? "" : "place-items-center" - }`} - > - {epiView === "1" - ? episode - .slice(firstEpisodeIndex, lastEpisodeIndex) - ?.map((epi, index) => { - const time = artStorage?.[epi?.id]?.time; - const duration = - artStorage?.[epi?.id]?.duration; - let prog = (time / duration) * 100; - if (prog > 90) prog = 100; - return ( - <Link - key={index} - href={`/en/anime/watch/${epi.id}/${info.id}/${prvValue}`} - 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 {epi?.number} - </span> - <span - className={`absolute bottom-7 left-0 h-1 bg-red-600`} - style={{ - width: - progress && - artStorage && - epi?.number <= progress - ? "100%" - : artStorage?.[epi?.id] - ? `${prog}%` - : "0%", - }} - /> - <div className="absolute inset-0 bg-black z-30 opacity-20" /> - <Image - src={epi?.image} - alt="epi image" - width={500} - height={500} - className="object-cover w-full h-[150px] sm:h-[100px] z-20" - /> - </Link> - ); - }) - : ""} - {epiView === "2" && - episode + <rect width="29" height="4" x="2" y="2" rx="2"></rect> + <rect width="29" height="4" x="2" y="8" rx="2"></rect> + <rect width="16" height="4" x="2" y="14" rx="2"></rect> + </svg> + </div> + </div> + </div> + </div> + {!loading ? ( + Array.isArray(episode) ? ( + episode && ( + <div + className={`${ + epiView === "3" && + "scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full overflow-y-scroll hover:scrollbar-thumb-[#2e2f37] h-[640px]" + }`} + > + {episode?.length !== 0 && episode ? ( + <div + className={`grid ${ + epiView === "1" + ? "grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5 lg:gap-8" + : "flex flex-col gap-5" + } pb-5 pt-2 lg:pt-0 ${ + epiView === "3" ? "" : "place-items-center" + }`} + > + {epiView === "1" + ? episode .slice(firstEpisodeIndex, lastEpisodeIndex) - .map((epi, index) => { + ?.map((epi, index) => { const time = artStorage?.[epi?.id]?.time; const duration = artStorage?.[epi?.id]?.duration; @@ -930,140 +877,182 @@ export default function Info({ info, color, api }) { <Link key={index} href={`/en/anime/watch/${epi.id}/${info.id}/${prvValue}`} - className="flex group h-[110px] lg:h-[160px] w-full rounded-lg transition-all duration-300 ease-out bg-secondary cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white" + 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" > - <div className="w-[43%] lg:w-[30%] relative shrink-0 z-40 rounded-lg overflow-hidden shadow-[4px_0px_5px_0px_rgba(0,0,0,0.3)]"> - <div className="relative"> - <Image - src={epi?.image} - alt="Anime Cover" - width={1000} - height={1000} - className="object-cover z-30 rounded-lg h-[110px] lg:h-[160px] brightness-[65%]" - /> - <span - className={`absolute bottom-0 left-0 h-[3px] bg-red-700`} - style={{ - width: - progress && - artStorage && - epi?.number <= progress - ? "100%" - : artStorage?.[epi?.id] - ? `${prog}%` - : "0", - }} - /> - <span className="absolute bottom-2 left-2 font-karla font-semibold text-sm lg:text-lg"> - Episode {epi?.number} - </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 - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 20 20" - fill="currentColor" - className="w-5 h-5 invisible group-hover:visible" - > - <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> + <span className="absolute text-sm z-40 bottom-1 left-2 font-karla font-semibold text-white"> + Episode {epi?.number} + </span> + <span + className={`absolute bottom-7 left-0 h-1 bg-red-600`} + style={{ + width: + progress && + artStorage && + epi?.number <= progress + ? "100%" + : artStorage?.[epi?.id] + ? `${prog}%` + : "0%", + }} + /> + <div className="absolute inset-0 bg-black z-30 opacity-20" /> + <Image + src={epi?.image} + alt="epi image" + width={500} + height={500} + className="object-cover w-full h-[150px] sm:h-[100px] z-20" + /> + </Link> + ); + }) + : ""} + {epiView === "2" && + episode + .slice(firstEpisodeIndex, lastEpisodeIndex) + .map((epi, index) => { + const time = artStorage?.[epi?.id]?.time; + const duration = + artStorage?.[epi?.id]?.duration; + let prog = (time / duration) * 100; + if (prog > 90) prog = 100; + return ( + <Link + key={index} + href={`/en/anime/watch/${epi.id}/${info.id}/${prvValue}`} + className="flex group h-[110px] lg:h-[160px] w-full rounded-lg transition-all duration-300 ease-out bg-secondary cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white" + > + <div className="w-[43%] lg:w-[30%] relative shrink-0 z-40 rounded-lg overflow-hidden shadow-[4px_0px_5px_0px_rgba(0,0,0,0.3)]"> + <div className="relative"> + <Image + src={epi?.image} + alt="Anime Cover" + width={1000} + height={1000} + className="object-cover z-30 rounded-lg h-[110px] lg:h-[160px] brightness-[65%]" + /> + <span + className={`absolute bottom-0 left-0 h-[3px] bg-red-700`} + style={{ + width: + progress && + artStorage && + epi?.number <= progress + ? "100%" + : artStorage?.[epi?.id] + ? `${prog}%` + : "0", + }} + /> + <span className="absolute bottom-2 left-2 font-karla font-semibold text-sm lg:text-lg"> + Episode {epi?.number} + </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 + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + className="w-5 h-5 invisible group-hover:visible" + > + <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-[70%] h-full select-none p-4 flex flex-col justify-center gap-5 ${ - epi?.id == id ? "text-[#7a7a7a]" : "" - }`} - > - <h1 className="font-karla font-bold text-base lg:text-lg xl:text-xl italic line-clamp-1"> - {epi?.title} - </h1> - {epi?.description && ( - <p className="line-clamp-2 text-xs lg:text-md xl:text-lg italic font-outfit font-extralight"> - {epi?.description} - </p> - )} - </div> - </Link> - ); - })} - {epiView === "3" && - episode - .slice(firstEpisodeIndex, lastEpisodeIndex) - .map((epi, index) => { - return ( <div - key={index} - className="flex flex-col gap-3 px-2" + className={`w-[70%] h-full select-none p-4 flex flex-col justify-center gap-5 ${ + epi?.id == id ? "text-[#7a7a7a]" : "" + }`} > - <Link - href={`/en/anime/watch/${epi.id}/${info.id}/${prvValue}`} - className={`text-start text-sm lg:text-lg ${ - progress && epi.number <= progress - ? "text-[#5f5f5f]" - : "text-white" - }`} - > - <p>Episode {epi.number}</p> - {epi.title && ( - <p - className={`text-xs lg:text-sm ${ - progress && epi.number <= progress - ? "text-[#5f5f5f]" - : "text-[#b1b1b1]" - } italic`} - > - "{epi.title}" - </p> - )} - </Link> - {index !== episode?.length - 1 && ( - <span className="h-[1px] bg-white" /> + <h1 className="font-karla font-bold text-base lg:text-lg xl:text-xl italic line-clamp-1"> + {epi?.title} + </h1> + {epi?.description && ( + <p className="line-clamp-2 text-xs lg:text-md xl:text-lg italic font-outfit font-extralight"> + {epi?.description} + </p> )} </div> - ); - })} - </div> - ) : ( - <p>No Episodes Available</p> - )} - </div> - ) - ) : ( - <div className="flex flex-col"> - <pre - className={`rounded-md overflow-hidden ${getLanguageClassName( - "bash" - )}`} - > - <code>{episode?.message}</code> - </pre> + </Link> + ); + })} + {epiView === "3" && + episode + .slice(firstEpisodeIndex, lastEpisodeIndex) + .map((epi, index) => { + return ( + <div + key={index} + className="flex flex-col gap-3 px-2" + > + <Link + href={`/en/anime/watch/${epi.id}/${info.id}/${prvValue}`} + className={`text-start text-sm lg:text-lg ${ + progress && epi.number <= progress + ? "text-[#5f5f5f]" + : "text-white" + }`} + > + <p>Episode {epi.number}</p> + {epi.title && ( + <p + className={`text-xs lg:text-sm ${ + progress && epi.number <= progress + ? "text-[#5f5f5f]" + : "text-[#b1b1b1]" + } italic`} + > + "{epi.title}" + </p> + )} + </Link> + {index !== episode?.length - 1 && ( + <span className="h-[1px] bg-white" /> + )} + </div> + ); + })} + </div> + ) : ( + <p>No Episodes Available</p> + )} </div> ) ) : ( - <div className="flex justify-center"> - <div className="lds-ellipsis"> - <div></div> - <div></div> - <div></div> - <div></div> - </div> + <div className="flex flex-col"> + <pre + className={`rounded-md overflow-hidden ${getLanguageClassName( + "bash" + )}`} + > + <code>{episode?.message}</code> + </pre> </div> - )} - </div> + ) + ) : ( + <div className="flex justify-center"> + <div className="lds-ellipsis"> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + </div> + )} </div> - {info && rec?.length !== 0 && ( - <div className="w-screen lg:w-[90%] xl:w-[85%]"> - <Content - ids="recommendAnime" - section="Recommendations" - data={rec} - /> - </div> - )} </div> - </Layout> - </SkeletonTheme> + {info && rec?.length !== 0 && ( + <div className="w-screen lg:w-[90%] xl:w-[85%]"> + <Content + ids="recommendAnime" + section="Recommendations" + data={rec} + /> + </div> + )} + </div> + </Layout> </> ); } diff --git a/pages/en/anime/watch/[...info].js b/pages/en/anime/watch/[...info].js index d6e40e1..17ec5f7 100644 --- a/pages/en/anime/watch/[...info].js +++ b/pages/en/anime/watch/[...info].js @@ -7,7 +7,7 @@ import dynamic from "next/dynamic"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../../../api/auth/[...nextauth]"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import Skeleton from "react-loading-skeleton"; import { ChevronDownIcon, ForwardIcon } from "@heroicons/react/24/solid"; import { useRouter } from "next/router"; @@ -222,389 +222,384 @@ export default function Info({ sessions, id, aniId, provider, proxy, api }) { <title>{playingTitle || "Loading..."}</title> </Head> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - <div className="bg-primary"> - <Navigasi /> - <div className="min-h-screen mt-3 md:mt-0 flex flex-col lg:gap-0 gap-5 lg:flex-row lg:py-10 lg:px-10 justify-start w-screen"> - <div className="w-screen lg:w-[67%]"> - {loading ? ( - Array.isArray(epiData?.sources) ? ( - <div className="aspect-video z-20 bg-black"> - <VideoPlayer - key={id} - data={epiData} - id={id} - progress={parseInt(playingEpisode)} - session={sessions} - aniId={parseInt(data?.id)} - stats={statusWatch} - op={skip.op} - ed={skip.ed} - title={playingTitle} - poster={poster} - proxy={proxy} - provider={provider} - /> - </div> - ) : ( - <div className="aspect-video bg-black flex-center select-none"> - <p className="lg:p-0 p-5 text-center"> - Whoops! Something went wrong. Please reload the page or - try other sources. {`:(`} - </p> - </div> - ) + <div className="bg-primary"> + <Navigasi /> + <div className="min-h-screen mt-3 md:mt-0 flex flex-col lg:gap-0 gap-5 lg:flex-row lg:py-10 lg:px-10 justify-start w-screen"> + <div className="w-screen lg:w-[67%]"> + {loading ? ( + Array.isArray(epiData?.sources) ? ( + <div className="aspect-video z-20 bg-black"> + <VideoPlayer + key={id} + data={epiData} + id={id} + progress={parseInt(playingEpisode)} + session={sessions} + aniId={parseInt(data?.id)} + stats={statusWatch} + op={skip.op} + ed={skip.ed} + title={playingTitle} + poster={poster} + proxy={proxy} + provider={provider} + /> + </div> ) : ( - <div className="aspect-video bg-black" /> - )} - <div> - {data && data?.episodes.length > 0 ? ( - data.episodes - .filter((items) => items.id == id) - .map((item, index) => ( - <div className="flex justify-between" key={index}> - <div key={item.id} className="p-3 grid gap-2 w-[60%]"> - <div className="text-xl font-outfit font-semibold line-clamp-1"> - <Link - href={`/en/anime/${data.id}`} - className="inline hover:underline" - > - {item.title || - data.title.romaji || - data.title.english} - </Link> - </div> - <h4 className="text-sm font-karla font-light"> - Episode {item.number} - </h4> - </div> - <div className="w-[50%] flex gap-4 items-center justify-end px-4"> - <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={item.number} - onChange={(e) => { - const selectedEpisode = data.episodes.find( - (episode) => - episode.number === parseInt(e.target.value) - ); - router.push( - `/en/anime/watch/${selectedEpisode.id}/${data.id}` - ); - }} - > - {data.episodes.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 - className={`${ - item.number === data.episodes.length - ? "pointer-events-none" - : "" - } relative group`} - onClick={() => { - const currentEpisodeIndex = - data.episodes.findIndex( - (episode) => episode.number === item.number - ); - if ( - currentEpisodeIndex !== -1 && - currentEpisodeIndex < data.episodes.length - 1 - ) { - const nextEpisode = - data.episodes[currentEpisodeIndex + 1]; - router.push( - `/en/anime/watch/${nextEpisode.id}/${data.id}` - ); - } - }} + <div className="aspect-video bg-black flex-center select-none"> + <p className="lg:p-0 p-5 text-center"> + Whoops! Something went wrong. Please reload the page or try + other sources. {`:(`} + </p> + </div> + ) + ) : ( + <div className="aspect-video bg-black" /> + )} + <div> + {data && data?.episodes.length > 0 ? ( + data.episodes + .filter((items) => items.id == id) + .map((item, index) => ( + <div className="flex justify-between" key={index}> + <div key={item.id} className="p-3 grid gap-2 w-[60%]"> + <div className="text-xl font-outfit font-semibold line-clamp-1"> + <Link + href={`/en/anime/${data.id}`} + className="inline hover:underline" > - <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" /> - </button> + {item.title || + data.title.romaji || + data.title.english} + </Link> </div> + <h4 className="text-sm font-karla font-light"> + Episode {item.number} + </h4> </div> - )) - ) : ( - <div className="p-3 grid gap-2"> - <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> - )} - <div className="h-[1px] bg-[#3b3b3b]" /> - - <div className="px-4 pt-7 pb-4 h-full flex"> - <div className="aspect-[9/13] h-[240px]"> - {data ? ( - <Image - src={data.image} - 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 px-5 gap-3 h-[240px]"> - <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"> - {data ? data.studios : <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" - className="w-8 h-8 hover:fill-white hover:cursor-pointer" + <div className="w-[50%] flex gap-4 items-center justify-end px-4"> + <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={item.number} + onChange={(e) => { + const selectedEpisode = data.episodes.find( + (episode) => + episode.number === parseInt(e.target.value) + ); + router.push( + `/en/anime/watch/${selectedEpisode.id}/${data.id}` + ); + }} > - <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> + {data.episodes.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 + className={`${ + item.number === data.episodes.length + ? "pointer-events-none" + : "" + } relative group`} + onClick={() => { + const currentEpisodeIndex = data.episodes.findIndex( + (episode) => episode.number === item.number + ); + if ( + currentEpisodeIndex !== -1 && + currentEpisodeIndex < data.episodes.length - 1 + ) { + const nextEpisode = + data.episodes[currentEpisodeIndex + 1]; + router.push( + `/en/anime/watch/${nextEpisode.id}/${data.id}` + ); + } + }} + > + <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" /> + </button> </div> </div> - <div className="grid gap-1 items-center"> - <h2 className="text-sm font-light font-roboto text-[#878787]"> - Status - </h2> - <div>{data ? data.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"> - {data ? ( - <> - <div className="line-clamp-3"> - {data.title.romaji || ""} - </div> - <div className="line-clamp-3"> - {data.title.english || ""} - </div> - <div className="line-clamp-3"> - {data.title.native || ""} - </div> - </> - ) : ( - <Skeleton width={200} height={50} /> - )} - </div> + )) + ) : ( + <div className="p-3 grid gap-2"> + <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> - <div className="flex flex-wrap gap-3 px-4 pt-3"> - {data && - data.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`}> - {data && ( - <p - dangerouslySetInnerHTML={{ __html: data.description }} - className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `} + )} + <div className="h-[1px] bg-[#3b3b3b]" /> + + <div className="px-4 pt-7 pb-4 h-full flex"> + <div className="aspect-[9/13] h-[240px]"> + {data ? ( + <Image + src={data.image} + alt="Anime Cover" + width={1000} + height={1000} + priority + className="object-cover aspect-[9/13] h-[240px] rounded-md" /> + ) : ( + <Skeleton height={240} /> )} </div> - {!showComments && loading && ( - <div className="w-full flex justify-center py-5 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> - {data && url && playing && ( - <div className="mt-5 px-5"> - <DisqusComments - key={id} - post={{ - id: id, - title: data.title.romaji, - url: url, - episode: playing.number, - }} - /> + <div className="grid w-full px-5 gap-3 h-[240px]"> + <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"> + {data ? data.studios : <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" + className="w-8 h-8 hover:fill-white hover:cursor-pointer" + > + <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>{data ? data.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"> + {data ? ( + <> + <div className="line-clamp-3"> + {data.title.romaji || ""} + </div> + <div className="line-clamp-3"> + {data.title.english || ""} + </div> + <div className="line-clamp-3"> + {data.title.native || ""} + </div> + </> + ) : ( + <Skeleton width={200} height={50} /> + )} + </div> + </div> + </div> + </div> + <div className="flex flex-wrap gap-3 px-4 pt-3"> + {data && + data.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`}> + {data && ( + <p + dangerouslySetInnerHTML={{ __html: data.description }} + className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `} + /> )} </div> + {!showComments && loading && ( + <div className="w-full flex justify-center py-5 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> + {data && url && playing && ( + <div className="mt-5 px-5"> + <DisqusComments + key={id} + post={{ + id: id, + title: data.title.romaji, + url: url, + episode: playing.number, + }} + /> + </div> + )} + </div> + )} </div> - <div className="flex flex-col w-screen lg:w-[35%] "> - <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 px-2 py-2 scrollbar-thin scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full"> - {data && data?.episodes.length > 0 ? ( - data.episodes.some( - (item) => item.title && item.description - ) ? ( - episodes.map((item) => { - const time = artStorage?.[item.id]?.time; - const duration = artStorage?.[item.id]?.duration; - let prog = (time / duration) * 100; - if (prog > 90) prog = 100; - return ( - <Link - href={`/en/anime/watch/${item.id}/${data.id}${ - provider ? `/${provider}` : "" - }`} - key={item.id} - className={`bg-secondary flex w-full h-[110px] rounded-lg scale-100 transition-all duration-300 ease-out ${ - item.id == id - ? "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"> - <Image - src={item.image} - alt="Anime Cover" - width={1000} - height={1000} - className={`object-cover z-30 rounded-lg h-[110px] ${ - item.id == id - ? "brightness-[30%]" - : "brightness-75" - }`} - /> - <span - className={`absolute bottom-0 left-0 h-[3px] 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 == id && ( - <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-[70%] h-full select-none p-4 flex flex-col gap-2 ${ - item.id == id ? "text-[#7a7a7a]" : "" - }`} - > - <h1 className="font-karla font-bold italic line-clamp-1"> - {item.title} - </h1> - <p className="line-clamp-2 text-xs italic font-outfit font-extralight"> - {item.description} - </p> + </div> + <div className="flex flex-col w-screen lg:w-[35%] "> + <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 px-2 py-2 scrollbar-thin scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full"> + {data && data?.episodes.length > 0 ? ( + data.episodes.some((item) => item.title && item.description) ? ( + episodes.map((item) => { + const time = artStorage?.[item.id]?.time; + const duration = artStorage?.[item.id]?.duration; + let prog = (time / duration) * 100; + if (prog > 90) prog = 100; + return ( + <Link + href={`/en/anime/watch/${item.id}/${data.id}${ + provider ? `/${provider}` : "" + }`} + key={item.id} + className={`bg-secondary flex w-full h-[110px] rounded-lg scale-100 transition-all duration-300 ease-out ${ + item.id == id + ? "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"> + <Image + src={item.image} + alt="Anime Cover" + width={1000} + height={1000} + className={`object-cover z-30 rounded-lg h-[110px] ${ + item.id == id + ? "brightness-[30%]" + : "brightness-75" + }`} + /> + <span + className={`absolute bottom-0 left-0 h-[3px] 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 == id && ( + <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> - </Link> - ); - }) - ) : ( - data.episodes.map((item) => { - return ( - <Link - href={`/en/anime/watch/${item.id}/${data.id}${ - provider ? "/9anime" : "" - }`} - key={item.id} - className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${ - item.id == id - ? "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" + </div> + <div + className={`w-[70%] h-full select-none p-4 flex flex-col gap-2 ${ + item.id == id ? "text-[#7a7a7a]" : "" }`} > - Episode {item.number} - </Link> - ); - }) - ) + <h1 className="font-karla font-bold italic line-clamp-1"> + {item.title} + </h1> + <p className="line-clamp-2 text-xs italic font-outfit font-extralight"> + {item.description} + </p> + </div> + </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> + data.episodes.map((item) => { + return ( + <Link + href={`/en/anime/watch/${item.id}/${data.id}${ + provider ? "/9anime" : "" + }`} + key={item.id} + className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${ + item.id == id + ? "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> </div> - </SkeletonTheme> + </div> </> ); } diff --git a/pages/en/search/[param].js b/pages/en/search/[param].js index 190002d..cacc2b8 100644 --- a/pages/en/search/[param].js +++ b/pages/en/search/[param].js @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { AnimatePresence, motion as m } from "framer-motion"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import Skeleton from "react-loading-skeleton"; import { useRouter } from "next/router"; import Link from "next/link"; import Navbar from "../../../components/navbar"; @@ -462,18 +462,16 @@ export default function Card() { {loading && ( <> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - {[1, 2, 4, 5, 6, 7, 8].map((item) => ( - <div - key={item} - className="flex flex-col w-[135px] xl:w-[185px] gap-5" - style={{ scale: 0.98 }} - > - <Skeleton className="h-[192px] w-[135px] xl:h-[265px] xl:w-[185px]" /> - <Skeleton width={110} height={30} /> - </div> - ))} - </SkeletonTheme> + {[1, 2, 4, 5, 6, 7, 8].map((item) => ( + <div + key={item} + className="flex flex-col w-[135px] xl:w-[185px] gap-5" + style={{ scale: 0.98 }} + > + <Skeleton className="h-[192px] w-[135px] xl:h-[265px] xl:w-[185px]" /> + <Skeleton width={110} height={30} /> + </div> + ))} </> )} </div> diff --git a/pages/id/anime/[...id].js b/pages/id/anime/[...id].js index 8a52e4b..e5a26f8 100644 --- a/pages/id/anime/[...id].js +++ b/pages/id/anime/[...id].js @@ -1,4 +1,4 @@ -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import Skeleton from "react-loading-skeleton"; import { ChevronDownIcon, @@ -291,123 +291,60 @@ export default function Info({ info, color, api }) { )} </div> </Modal> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - <Layout navTop="text-white bg-primary lg:pt-0 lg:px-0 bg-slate bg-opacity-40 z-50"> - <div className="w-screen min-h-screen relative flex flex-col items-center bg-primary gap-5"> - <div className="bg-image w-screen"> - <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[300px] w-screen z-10 inset-0" /> - {info ? ( - <Image - src={ - info?.bannerImage || - info?.coverImage?.extraLarge || - info?.coverImage.large - } - priority={true} - alt="banner anime" - height={1000} - width={1000} - className="object-cover bg-image w-screen absolute top-0 left-0 h-[300px] brightness-[70%] z-0" - /> - ) : ( - <div className="bg-image w-screen absolute top-0 left-0 h-[300px]" /> - )} - </div> - <div className="lg:w-[90%] xl:w-[75%] lg:pt-[10rem] z-30 flex flex-col gap-5"> - {/* Mobile */} + <Layout navTop="text-white bg-primary lg:pt-0 lg:px-0 bg-slate bg-opacity-40 z-50"> + <div className="w-screen min-h-screen relative flex flex-col items-center bg-primary gap-5"> + <div className="bg-image w-screen"> + <div className="bg-gradient-to-t from-primary from-10% to-transparent absolute h-[300px] w-screen z-10 inset-0" /> + {info ? ( + <Image + src={ + info?.bannerImage || + info?.coverImage?.extraLarge || + info?.coverImage.large + } + priority={true} + alt="banner anime" + height={1000} + width={1000} + className="object-cover bg-image w-screen absolute top-0 left-0 h-[300px] brightness-[70%] z-0" + /> + ) : ( + <div className="bg-image w-screen absolute top-0 left-0 h-[300px]" /> + )} + </div> + <div className="lg:w-[90%] xl:w-[75%] lg:pt-[10rem] z-30 flex flex-col gap-5"> + {/* Mobile */} - <div className="lg:hidden pt-5 w-screen px-5 flex flex-col"> - <div className="h-[250px] flex flex-col gap-1 justify-center"> - <h1 className="font-karla font-extrabold text-lg line-clamp-1 w-[70%]"> - {info?.title?.romaji || info?.title?.english} - </h1> - <p - className="line-clamp-2 text-sm font-light antialiased w-[56%]" - dangerouslySetInnerHTML={{ __html: info?.description }} - /> - <div className="font-light flex gap-1 py-1 flex-wrap font-outfit text-[10px] text-[#ffffff] w-[70%]"> - {info?.genres - ?.slice( - 0, - info?.genres?.length > 3 ? info?.genres?.length : 3 - ) - .map((item, index) => ( - <span - key={index} - className="px-2 py-1 bg-secondary shadow-lg font-outfit font-light rounded-full" - > - <span className="">{item}</span> - </span> - ))} - </div> - {info && ( - <div className="flex items-center gap-5 pt-3 text-center"> - <div className="flex items-center gap-2 text-center"> - <button - type="button" - className="bg-action px-10 rounded-sm font-karla font-bold" - onClick={() => handleOpen()} - > - {!loading - ? statuses - ? statuses.name - : "Add to List" - : "Loading..."} - </button> - <div className="h-6 w-6"> - <HeartIcon /> - </div> - </div> - </div> - )} - </div> - <div className="bg-secondary rounded-sm xs:h-[30px]"> - <div className="grid grid-cols-3 place-content-center xxs:flex items-center justify-center h-full xxs:gap-10 p-2 text-sm"> - {info && info.status !== "NOT_YET_RELEASED" ? ( - <> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <TvIcon className="w-5 h-5 text-action" /> - <h4 className="font-karla">{info?.type}</h4> - </div> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <ArrowTrendingUpIcon className="w-5 h-5 text-action" /> - <h4>{info?.averageScore}%</h4> - </div> - <div className="flex-center flex-col xxs:flex-row gap-2"> - <RectangleStackIcon className="w-5 h-5 text-action" /> - {info?.episodes ? ( - <h1>{info?.episodes} Episodes</h1> - ) : ( - <h1>TBA</h1> - )} - </div> - </> - ) : ( - <div>{info && "Not Yet Released"}</div> - )} - </div> + <div className="lg:hidden pt-5 w-screen px-5 flex flex-col"> + <div className="h-[250px] flex flex-col gap-1 justify-center"> + <h1 className="font-karla font-extrabold text-lg line-clamp-1 w-[70%]"> + {info?.title?.romaji || info?.title?.english} + </h1> + <p + className="line-clamp-2 text-sm font-light antialiased w-[56%]" + dangerouslySetInnerHTML={{ __html: info?.description }} + /> + <div className="font-light flex gap-1 py-1 flex-wrap font-outfit text-[10px] text-[#ffffff] w-[70%]"> + {info?.genres + ?.slice( + 0, + info?.genres?.length > 3 ? info?.genres?.length : 3 + ) + .map((item, index) => ( + <span + key={index} + className="px-2 py-1 bg-secondary shadow-lg font-outfit font-light rounded-full" + > + <span className="">{item}</span> + </span> + ))} </div> - </div> - - {/* PC */} - <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" - /> + {info && ( + <div className="flex items-center gap-5 pt-3 text-center"> + <div className="flex items-center gap-2 text-center"> <button type="button" - className="bg-action flex-center z-20 h-[20px] w-[180px] absolute bottom-0 rounded-sm font-karla font-bold" + className="bg-action px-10 rounded-sm font-karla font-bold" onClick={() => handleOpen()} > {!loading @@ -416,341 +353,400 @@ export default function Info({ info, color, api }) { : "Add to List" : "Loading..."} </button> + <div className="h-6 w-6"> + <HeartIcon /> + </div> + </div> + </div> + )} + </div> + <div className="bg-secondary rounded-sm xs:h-[30px]"> + <div className="grid grid-cols-3 place-content-center xxs:flex items-center justify-center h-full xxs:gap-10 p-2 text-sm"> + {info && info.status !== "NOT_YET_RELEASED" ? ( + <> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <TvIcon className="w-5 h-5 text-action" /> + <h4 className="font-karla">{info?.type}</h4> + </div> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <ArrowTrendingUpIcon className="w-5 h-5 text-action" /> + <h4>{info?.averageScore}%</h4> + </div> + <div className="flex-center flex-col xxs:flex-row gap-2"> + <RectangleStackIcon className="w-5 h-5 text-action" /> + {info?.episodes ? ( + <h1>{info?.episodes} Episodes</h1> + ) : ( + <h1>TBA</h1> + )} + </div> </> ) : ( - <Skeleton className="h-[250px] w-[180px]" /> + <div>{info && "Not Yet Released"}</div> )} </div> + </div> + </div> - {/* PC */} - <div className="hidden lg:flex w-full flex-col gap-5 h-[250px]"> - <div className="flex flex-col gap-2"> - <h1 className=" font-inter font-bold text-[36px] text-white line-clamp-1"> - {info ? ( - info?.title?.romaji || info?.title?.english - ) : ( - <Skeleton width={450} /> - )} - </h1> + {/* PC */} + <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> + + {/* PC */} + <div className="hidden lg:flex w-full flex-col gap-5 h-[250px]"> + <div className="flex flex-col gap-2"> + <h1 className=" font-inter font-bold text-[36px] text-white line-clamp-1"> {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> - )} + 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} > - Sub | EN + {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> - ) : ( - <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]" - /> + </div> ) : ( - <Skeleton className="h-[130px]" /> + <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 grid lg:grid-cols-3 justify-items-center gap-7 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 + <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 grid lg:grid-cols-3 justify-items-center gap-7 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" + ? `/id/anime/${rel.id}` + : `/manga/detail/id?aniId=${ + rel.id + }&aniTitle=${encodeURIComponent( + info?.title?.english || + info?.title.romaji || + info?.title.native + )}` + } + className={`hover:scale-[1.02] hover:shadow-lg lg:px-0 px-4 scale-100 transition-transform duration-200 ease-out w-full ${ + rel.type === "MUSIC" ? "pointer-events-none" : "" + }`} + > + <div key={rel.id} - href={ - rel.type === "ANIME" || - rel.type === "OVA" || - rel.type === "MOVIE" || - rel.type === "SPECIAL" || - rel.type === "ONA" - ? `/id/anime/${rel.id}` - : `/manga/detail/id?aniId=${ - rel.id - }&aniTitle=${encodeURIComponent( - info?.title?.english || - info?.title.romaji || - info?.title.native - )}` - } - className={`hover:scale-[1.02] hover:shadow-lg lg:px-0 px-4 scale-100 transition-transform duration-200 ease-out w-full ${ - rel.type === "MUSIC" ? "pointer-events-none" : "" - }`} + className="w-full shrink h-[126px] bg-secondary flex rounded-md" > - <div - key={rel.id} - className="w-full shrink h-[126px] bg-secondary flex rounded-md" - > - <div className="w-[90px] bg-image rounded-l-md shrink-0"> - <Image - src={ - rel.coverImage.extraLarge || - rel.coverImage.large - } - alt={rel.id} - height={500} - width={500} - className="object-cover h-full w-full shrink-0 rounded-l-md" - /> + <div className="w-[90px] bg-image rounded-l-md shrink-0"> + <Image + src={ + rel.coverImage.extraLarge || + rel.coverImage.large + } + 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="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 || rel.title.romaji} - </div> - <div className={``}>{rel.type}</div> + <div className="font-outfit font-thin line-clamp-2"> + {rel.title.userPreferred || rel.title.romaji} </div> + <div className={``}>{rel.type}</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"> + </div> + </Link> + ); + }) + ) : ( + <> + {[1, 2, 3].map((item) => ( + <div key={item} className="w-full hidden lg:block"> <Skeleton className="h-[126px]" /> </div> - </> - )} - </div> + ))} + <div className="w-full lg:hidden"> + <Skeleton className="h-[126px]" /> + </div> + </> + )} </div> - <div className="flex flex-col gap-5 lg:gap-10 p-3 lg:p-0"> - <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"> - {time} - </div> - </div> - <div className="h-6 w-6"> - <ClockIcon /> + </div> + <div className="flex flex-col gap-5 lg:gap-10 p-3 lg:p-0"> + <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"> + {time} </div> </div> - )} - </div> - {episode?.length > 50 && ( - <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 className="h-6 w-6"> + <ClockIcon /> + </div> </div> )} </div> {episode?.length > 50 && ( <div - className={`flex lg:flex items-center gap-0 lg:gap-5 justify-between ${ - visible ? "" : "hidden" - }`} + className="lg:hidden bg-secondary p-1 rounded-md cursor-pointer" + onClick={() => setVisible(!visible)} > - <div className="flex items-end gap-3"> - {episode?.length > 50 && ( - <div className="relative flex gap-2 items-center"> - <p className="hidden md:block">Episodes</p> - <select - onChange={onEpisodeIndexChange} - value={selectedRange} - 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 scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded-lg" - > - <option value="All">All</option> - {[...Array(Math.ceil(episode?.length / 50))].map( - (_, index) => { - const start = index * 50 + 1; - const end = Math.min( - start + 50 - 1, - episode?.length - ); - const optionLabel = `${start} to ${end}`; - if (episode[0]?.number !== 1) { - var valueLabel = `${ - episode.length - end + 1 - }-${episode.length - start + 1}`; - } else { - var valueLabel = `${start}-${end}`; - } - return ( - <option key={valueLabel} value={valueLabel}> - {optionLabel} - </option> - ); - } - )} - </select> - <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> - </div> - )} - </div> + <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> - {!loading ? ( - Array.isArray(episode) ? ( - episode && ( - <div className="scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full overflow-y-scroll hover:scrollbar-thumb-[#2e2f37] h-[640px]"> - {episode?.length !== 0 && episode ? ( - <div - className={`flex flex-col gap-5 pb-5 pt-2 lg:pt-0`} + {episode?.length > 50 && ( + <div + className={`flex lg:flex items-center gap-0 lg:gap-5 justify-between ${ + visible ? "" : "hidden" + }`} + > + <div className="flex items-end gap-3"> + {episode?.length > 50 && ( + <div className="relative flex gap-2 items-center"> + <p className="hidden md:block">Episodes</p> + <select + onChange={onEpisodeIndexChange} + value={selectedRange} + 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 scrollbar-thin scrollbar-thumb-secondary scrollbar-thumb-rounded-lg" > - {episode - .slice(firstEpisodeIndex, lastEpisodeIndex) - .map((epi, index) => { + <option value="All">All</option> + {[...Array(Math.ceil(episode?.length / 50))].map( + (_, index) => { + const start = index * 50 + 1; + const end = Math.min( + start + 50 - 1, + episode?.length + ); + const optionLabel = `${start} to ${end}`; + if (episode[0]?.number !== 1) { + var valueLabel = `${ + episode.length - end + 1 + }-${episode.length - start + 1}`; + } else { + var valueLabel = `${start}-${end}`; + } return ( - <div - key={index} - className="flex flex-col gap-3 px-2" - > - <Link - href={`/id/anime/watch/${info.id}/${epi.episodeId}`} - className={`text-start text-sm lg:text-lg ${ - progress && index <= progress - 1 - ? "text-[#5f5f5f]" - : "text-white" - }`} - > - <p>{epi.epsTitle}</p> - </Link> - {index !== episode?.length - 1 && ( - <span className="h-[1px] bg-white" /> - )} - </div> + <option key={valueLabel} value={valueLabel}> + {optionLabel} + </option> ); - })} - </div> - ) : ( - <p>No Episodes Available</p> - )} - </div> - ) - ) : ( - <div className="flex flex-col"> - <pre - className={`rounded-md overflow-hidden ${getLanguageClassName( - "bash" - )}`} - > - <code> - {episode?.message || "Anime tidak tersedia :/"} - </code> - </pre> - </div> - ) - ) : ( - <div className="flex justify-center"> - <div className="lds-ellipsis"> - <div></div> - <div></div> - <div></div> - <div></div> + } + )} + </select> + <ChevronDownIcon className="absolute right-2 top-1/2 transform -translate-y-1/2 w-5 h-5 pointer-events-none" /> + </div> + )} </div> </div> )} </div> + {!loading ? ( + Array.isArray(episode) ? ( + episode && ( + <div className="scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full overflow-y-scroll hover:scrollbar-thumb-[#2e2f37] h-[640px]"> + {episode?.length !== 0 && episode ? ( + <div + className={`flex flex-col gap-5 pb-5 pt-2 lg:pt-0`} + > + {episode + .slice(firstEpisodeIndex, lastEpisodeIndex) + .map((epi, index) => { + return ( + <div + key={index} + className="flex flex-col gap-3 px-2" + > + <Link + href={`/id/anime/watch/${info.id}/${epi.episodeId}`} + className={`text-start text-sm lg:text-lg ${ + progress && index <= progress - 1 + ? "text-[#5f5f5f]" + : "text-white" + }`} + > + <p>{epi.epsTitle}</p> + </Link> + {index !== episode?.length - 1 && ( + <span className="h-[1px] bg-white" /> + )} + </div> + ); + })} + </div> + ) : ( + <p>No Episodes Available</p> + )} + </div> + ) + ) : ( + <div className="flex flex-col"> + <pre + className={`rounded-md overflow-hidden ${getLanguageClassName( + "bash" + )}`} + > + <code> + {episode?.message || "Anime tidak tersedia :/"} + </code> + </pre> + </div> + ) + ) : ( + <div className="flex justify-center"> + <div className="lds-ellipsis"> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + </div> + )} </div> - {info && rec?.length !== 0 && ( - <div className="w-screen lg:w-[90%] xl:w-[85%]"> - <Content - ids="recommendAnime" - section="Recommendations" - data={rec} - /> - </div> - )} </div> - </Layout> - </SkeletonTheme> + {info && rec?.length !== 0 && ( + <div className="w-screen lg:w-[90%] xl:w-[85%]"> + <Content + ids="recommendAnime" + section="Recommendations" + data={rec} + /> + </div> + )} + </div> + </Layout> </> ); } diff --git a/pages/id/anime/watch/[...info].js b/pages/id/anime/watch/[...info].js index 89fd3a6..06269ab 100644 --- a/pages/id/anime/watch/[...info].js +++ b/pages/id/anime/watch/[...info].js @@ -7,7 +7,7 @@ import dynamic from "next/dynamic"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../../../api/auth/[...nextauth]"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import Skeleton from "react-loading-skeleton"; import { Navigasi } from "../.."; import { ChevronDownIcon, ForwardIcon } from "@heroicons/react/24/solid"; @@ -198,261 +198,258 @@ export default function Info({ sessions, id, aniId, provider, api, proxy }) { <title>{playingTitle || "Loading..."}</title> </Head> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - <div className="bg-primary"> - <Navigasi /> - <div className="min-h-screen mt-3 md:mt-0 flex flex-col lg:gap-0 gap-5 lg:flex-row lg:py-10 lg:px-10 justify-start w-screen"> - <div className="w-screen lg:w-[67%]"> - {loading ? ( - Array.isArray(epiData) ? ( - <div className="aspect-video z-20 bg-black"> - <VideoPlayer - key={id} - data={epiData} - id={aniId} - progress={parseInt(playingEpisode)} - session={sessions} - aniId={parseInt(data?.id)} - stats={statusWatch} - op={skip.op} - ed={skip.ed} - title={playingTitle} - poster={poster[0]?.image} - proxy={proxy} - /> - </div> - ) : ( - <div className="aspect-video bg-black flex-center select-none"> - <p className="lg:p-0 p-5 text-center"> - Whoops! Something went wrong. Please reload the page or - try other sources. {`:(`} - </p> - </div> - ) + <div className="bg-primary"> + <Navigasi /> + <div className="min-h-screen mt-3 md:mt-0 flex flex-col lg:gap-0 gap-5 lg:flex-row lg:py-10 lg:px-10 justify-start w-screen"> + <div className="w-screen lg:w-[67%]"> + {loading ? ( + Array.isArray(epiData) ? ( + <div className="aspect-video z-20 bg-black"> + <VideoPlayer + key={id} + data={epiData} + id={aniId} + progress={parseInt(playingEpisode)} + session={sessions} + aniId={parseInt(data?.id)} + stats={statusWatch} + op={skip.op} + ed={skip.ed} + title={playingTitle} + poster={poster[0]?.image} + proxy={proxy} + /> + </div> ) : ( - <div className="aspect-video bg-black" /> - )} - <div> - {data && data?.episodes.length > 0 ? ( - data.episodes - .filter((items) => items.number == currentNumber) - .map((item, index) => ( - <div className="flex justify-between" key={item.id}> - <div className="p-3 grid gap-2 w-[60%]"> - <div className="text-xl font-outfit font-semibold line-clamp-1"> - <Link - href={`/id/anime/${data.id}`} - className="inline hover:underline" - > - {item.title || - data.title.romaji || - data.title.english} - </Link> - </div> - <h4 className="text-sm font-karla font-light"> - Episode {item.number} - </h4> - </div> - <div className="w-[50%] flex gap-4 items-center justify-end px-4"> - <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={item.number} - onChange={(e) => { - const selectedEpisode = data.episodes.find( - (episode) => - episode.number === parseInt(e.target.value) - ); - router.push( - `/id/anime/watch/${selectedEpisode.id}/${data.id}` - ); - }} - > - {data.episodes.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 - className={`${ - item.number === data.episodes.length - ? "pointer-events-none" - : "" - } relative group`} - onClick={() => { - const currentEpisodeIndex = - data.episodes.findIndex( - (episode) => episode.number === item.number - ); - if ( - currentEpisodeIndex !== -1 && - currentEpisodeIndex < data.episodes.length - 1 - ) { - const nextEpisode = - data.episodes[currentEpisodeIndex + 1]; - router.push( - `/id/anime/watch/${nextEpisode.id}/${data.id}` - ); - } - }} + <div className="aspect-video bg-black flex-center select-none"> + <p className="lg:p-0 p-5 text-center"> + Whoops! Something went wrong. Please reload the page or try + other sources. {`:(`} + </p> + </div> + ) + ) : ( + <div className="aspect-video bg-black" /> + )} + <div> + {data && data?.episodes.length > 0 ? ( + data.episodes + .filter((items) => items.number == currentNumber) + .map((item, index) => ( + <div className="flex justify-between" key={item.id}> + <div className="p-3 grid gap-2 w-[60%]"> + <div className="text-xl font-outfit font-semibold line-clamp-1"> + <Link + href={`/id/anime/${data.id}`} + className="inline hover:underline" > - <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" /> - </button> + {item.title || + data.title.romaji || + data.title.english} + </Link> </div> + <h4 className="text-sm font-karla font-light"> + Episode {item.number} + </h4> </div> - )) - ) : ( - <div className="p-3 grid gap-2"> - <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> - )} - <div className="h-[1px] bg-[#3b3b3b]" /> - - <div className="px-4 pt-7 pb-4 h-full flex"> - <div className="aspect-[9/13] h-[240px]"> - {data ? ( - <Image - src={data.image} - 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 px-5 gap-3 h-[240px]"> - <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"> - {data ? data.studios : <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" - className="w-8 h-8 hover:fill-white hover:cursor-pointer" + <div className="w-[50%] flex gap-4 items-center justify-end px-4"> + <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={item.number} + onChange={(e) => { + const selectedEpisode = data.episodes.find( + (episode) => + episode.number === parseInt(e.target.value) + ); + router.push( + `/id/anime/watch/${selectedEpisode.id}/${data.id}` + ); + }} > - <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> + {data.episodes.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 + className={`${ + item.number === data.episodes.length + ? "pointer-events-none" + : "" + } relative group`} + onClick={() => { + const currentEpisodeIndex = data.episodes.findIndex( + (episode) => episode.number === item.number + ); + if ( + currentEpisodeIndex !== -1 && + currentEpisodeIndex < data.episodes.length - 1 + ) { + const nextEpisode = + data.episodes[currentEpisodeIndex + 1]; + router.push( + `/id/anime/watch/${nextEpisode.id}/${data.id}` + ); + } + }} + > + <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" /> + </button> </div> </div> - <div className="grid gap-1 items-center"> - <h2 className="text-sm font-light font-roboto text-[#878787]"> - Status - </h2> - <div>{data ? data.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"> - {data ? ( - <> - <div className="line-clamp-3"> - {data.title.romaji || ""} - </div> - <div className="line-clamp-3"> - {data.title.english || ""} - </div> - <div className="line-clamp-3"> - {data.title.native || ""} - </div> - </> - ) : ( - <Skeleton width={200} height={50} /> - )} - </div> + )) + ) : ( + <div className="p-3 grid gap-2"> + <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> - <div className="flex flex-wrap gap-3 px-4 pt-3"> - {data && - data.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`}> - {data && ( - <p - dangerouslySetInnerHTML={{ __html: data.description }} - className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `} + )} + <div className="h-[1px] bg-[#3b3b3b]" /> + + <div className="px-4 pt-7 pb-4 h-full flex"> + <div className="aspect-[9/13] h-[240px]"> + {data ? ( + <Image + src={data.image} + 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 px-5 gap-3 h-[240px]"> + <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"> + {data ? data.studios : <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" + className="w-8 h-8 hover:fill-white hover:cursor-pointer" + > + <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>{data ? data.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"> + {data ? ( + <> + <div className="line-clamp-3"> + {data.title.romaji || ""} + </div> + <div className="line-clamp-3"> + {data.title.english || ""} + </div> + <div className="line-clamp-3"> + {data.title.native || ""} + </div> + </> + ) : ( + <Skeleton width={200} height={50} /> + )} + </div> + </div> + </div> </div> - </div> - <div className="flex flex-col w-screen lg:w-[35%] "> - <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 px-2 py-2 scrollbar-thin scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full"> - {data && data?.episodes.length > 0 ? ( - episode.map((item, index) => { - return ( - <Link - href={`/id/anime/watch/${data.id}/${item.episodeId}`} - key={item.id} - className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${ - index === currentNumber - 1 - ? "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 {index + 1} - </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 className="flex flex-wrap gap-3 px-4 pt-3"> + {data && + data.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`}> + {data && ( + <p + dangerouslySetInnerHTML={{ __html: data.description }} + className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `} + /> )} </div> </div> </div> + <div className="flex flex-col w-screen lg:w-[35%] "> + <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 px-2 py-2 scrollbar-thin scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full"> + {data && data?.episodes.length > 0 ? ( + episode.map((item, index) => { + return ( + <Link + href={`/id/anime/watch/${data.id}/${item.episodeId}`} + key={item.id} + className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${ + index === currentNumber - 1 + ? "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 {index + 1} + </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> </div> - </SkeletonTheme> + </div> </> ); } diff --git a/pages/id/search/[param].js b/pages/id/search/[param].js index 00fab64..43f419c 100644 --- a/pages/id/search/[param].js +++ b/pages/id/search/[param].js @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { AnimatePresence, motion as m } from "framer-motion"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import Skeleton from "react-loading-skeleton"; import { useRouter } from "next/router"; import Link from "next/link"; import Navbar from "../../../components/navbar"; @@ -460,18 +460,16 @@ export default function Card() { {loading && ( <> - <SkeletonTheme baseColor="#232329" highlightColor="#2a2a32"> - {[1, 2, 4, 5, 6, 7, 8].map((item) => ( - <div - key={item} - className="flex flex-col w-[135px] xl:w-[185px] gap-5" - style={{ scale: 0.98 }} - > - <Skeleton className="h-[192px] w-[135px] xl:h-[265px] xl:w-[185px]" /> - <Skeleton width={110} height={30} /> - </div> - ))} - </SkeletonTheme> + {[1, 2, 4, 5, 6, 7, 8].map((item) => ( + <div + key={item} + className="flex flex-col w-[135px] xl:w-[185px] gap-5" + style={{ scale: 0.98 }} + > + <Skeleton className="h-[192px] w-[135px] xl:h-[265px] xl:w-[185px]" /> + <Skeleton width={110} height={30} /> + </div> + ))} </> )} </div> |