diff options
| author | Factiven <[email protected]> | 2023-05-16 22:40:02 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-16 22:40:02 +0700 |
| commit | 9a5754fdba9d778f820fe89b44d1e21ca9f0bb4d (patch) | |
| tree | 8bd574163e760216bc91f7b3c164232b6982efe8 | |
| parent | Update v3.5.6 (diff) | |
| download | moopa-9a5754fdba9d778f820fe89b44d1e21ca9f0bb4d.tar.xz moopa-9a5754fdba9d778f820fe89b44d1e21ca9f0bb4d.zip | |
Update v3.5.7 (#12)
* Merge request (#11)
* Update v3.5.5
> Now Skip button will hide if player is not in focused state.
> Added some options to player.
> Manga images should be displayed now.
* Update videoPlayer.js
* Revamp hero section #1
* UI Improvement
> Updating main page
> Updated Genres selection using params method
> Added search bar v1.0 on main page ( [ctrl + space] to access search bar )
* update meta
* Update [...id].js
* Update [...id].js
> Back to ssr I guess
* update episode selector
* Update [...info].js
* Update UI
> Added On-Going section for AniList user
* Update content.js
* added dynamic og
* Update og.jsx
* Update og
* Update og.jsx
* update og and id fallback
> Added fallback for anime info if it's not found
* Update v3.5.7
> Added On-Going section for AniList user
> Added Genre section
> Added dynamic Open Graph when sharing anime
> Added Episode Selector above information
| -rw-r--r-- | assets/Karla-MediumItalic.ttf | bin | 0 -> 42252 bytes | |||
| -rw-r--r-- | assets/Outfit-Regular.ttf | bin | 0 -> 47848 bytes | |||
| -rw-r--r-- | components/hero/content.js | 182 | ||||
| -rw-r--r-- | components/hero/genres.js | 69 | ||||
| -rw-r--r-- | components/searchBar.js | 144 | ||||
| -rw-r--r-- | components/videoPlayer.js | 2 | ||||
| -rw-r--r-- | lib/useAnilist.js | 33 | ||||
| -rw-r--r-- | package-lock.json | 175 | ||||
| -rw-r--r-- | package.json | 3 | ||||
| -rw-r--r-- | pages/anime/[...id].js | 129 | ||||
| -rw-r--r-- | pages/api/og.jsx | 103 | ||||
| -rw-r--r-- | pages/index.js | 60 | ||||
| -rw-r--r-- | pages/search/[param].js | 44 |
13 files changed, 841 insertions, 103 deletions
diff --git a/assets/Karla-MediumItalic.ttf b/assets/Karla-MediumItalic.ttf Binary files differnew file mode 100644 index 0000000..608d425 --- /dev/null +++ b/assets/Karla-MediumItalic.ttf diff --git a/assets/Outfit-Regular.ttf b/assets/Outfit-Regular.ttf Binary files differnew file mode 100644 index 0000000..3fe424f --- /dev/null +++ b/assets/Outfit-Regular.ttf diff --git a/components/hero/content.js b/components/hero/content.js index 7e2d9ab..24ee942 100644 --- a/components/hero/content.js +++ b/components/hero/content.js @@ -1,9 +1,54 @@ import Link from "next/link"; -import React, { useState } from "react"; +import React, { useState, useRef, useEffect } from "react"; import Image from "next/image"; -import { MdChevronLeft, MdChevronRight } from "react-icons/md"; +import { MdChevronRight } from "react-icons/md"; +import { + ChevronRightIcon, + ArrowRightCircleIcon, +} from "@heroicons/react/24/outline"; + +import { ChevronLeftIcon } from "@heroicons/react/20/solid"; +import { ExclamationCircleIcon } from "@heroicons/react/24/solid"; + +export default function Content({ ids, section, data, og }) { + const [startX, setStartX] = useState(null); + const [scrollLefts, setScrollLefts] = useState(null); + const containerRef = useRef(null); + + const [isDragging, setIsDragging] = useState(false); + const [clicked, setClicked] = useState(false); + + useEffect(() => { + const click = localStorage.getItem("clicked"); + if (click) { + setClicked(JSON.parse(click)); + } + }, []); + + const handleMouseDown = (e) => { + setIsDragging(true); + setStartX(e.pageX - containerRef.current.offsetLeft); + setScrollLefts(containerRef.current.scrollLeft); + }; + + const handleMouseUp = () => { + setIsDragging(false); + }; + + const handleMouseMove = (e) => { + if (!isDragging) return; + e.preventDefault(); + const x = e.pageX - containerRef.current.offsetLeft; + const walk = (x - startX) * 3; + containerRef.current.scrollLeft = scrollLeft - walk; + }; + + const handleClick = (e) => { + if (isDragging) { + e.preventDefault(); + } + }; -export default function Content({ ids, section, data }) { const [scrollLeft, setScrollLeft] = useState(false); const [scrollRight, setScrollRight] = useState(true); @@ -24,27 +69,59 @@ export default function Content({ ids, section, data }) { setScrollRight(scrollRight); }; - // console.log({ left: scrollLeft, right: scrollRight }); + function handleAlert(e) { + if (localStorage.getItem("clicked")) { + const existingDataString = localStorage.getItem("clicked"); + const existingData = JSON.parse(existingDataString); + + existingData[e] = true; + + const updatedDataString = JSON.stringify(existingData); + + localStorage.setItem("clicked", updatedDataString); + } else { + const newData = { + [e]: true, + }; + + const newDataString = JSON.stringify(newData); + + localStorage.setItem("clicked", newDataString); + } + } const array = data; let filteredData = array?.filter((item) => item !== null); + const slicedData = + filteredData?.length > 15 ? filteredData?.slice(0, 15) : filteredData; + return ( <div> - <h1 className="px-5 font-karla text-[20px] font-bold">{section}</h1> + <div className="flex items-center justify-between lg:justify-normal lg:gap-3 px-5"> + <h1 className="font-karla text-[20px] font-bold">{section}</h1> + <ChevronRightIcon className="w-5 h-5" /> + </div> <div className="relative flex items-center lg:gap-2"> - <MdChevronLeft + <div onClick={slideLeft} - size={35} - className={`mb-5 cursor-pointer hover:text-action absolute left-0 bg-gradient-to-r from-[#141519] z-40 h-full hover:opacity-100 ${ - scrollLeft ? "visible" : "hidden" + className={`flex items-center mb-5 cursor-pointer hover:text-action absolute left-0 bg-gradient-to-r from-[#141519] z-40 h-full hover:opacity-100 ${ + scrollLeft ? "lg:visible" : "invisible" }`} - /> + > + <ChevronLeftIcon className="w-7 h-7 stroke-2" /> + </div> <div id={ids} - className="scroll flex h-full w-full items-center select-none overflow-x-scroll scroll-smooth whitespace-nowrap overflow-y-hidden scrollbar-hide lg:gap-8 gap-5 p-10 z-30 " + className="scroll flex h-full w-full items-center select-none overflow-x-scroll whitespace-nowrap overflow-y-hidden scrollbar-hide lg:gap-8 gap-3 lg:p-10 py-8 px-5 z-30 scroll-smooth" onScroll={handleScroll} + onMouseDown={handleMouseDown} + onMouseUp={handleMouseUp} + onMouseMove={handleMouseMove} + onClick={handleClick} + ref={containerRef} > - {filteredData?.map((anime) => { + {slicedData?.map((anime) => { + const progress = og?.find((i) => i.mediaId === anime.id); return ( <div key={anime.id} @@ -52,8 +129,39 @@ export default function Content({ ids, section, data }) { > <Link href={`/anime/${anime.id}`} - className="hover:scale-105 group relative duration-300 ease-in-out" + className="hover:scale-105 hover:shadow-lg group relative duration-300 ease-out" > + {ids === "onGoing" && ( + <div className="h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] bg-gradient-to-b from-transparent to-black absolute z-40 rounded-md whitespace-normal font-karla group"> + <div className="flex flex-col items-center h-full justify-end text-center pb-5"> + <h1 className="line-clamp-1 w-[70%] text-[10px]"> + {anime.title.romaji || anime.title.english} + </h1> + {checkProgress(progress) && + !clicked?.hasOwnProperty(anime.id) && ( + <ExclamationCircleIcon className="w-7 h-7 absolute z-40 -top-3 -right-3" /> + )} + {checkProgress(progress) && ( + <div + onClick={() => handleAlert(anime.id)} + className="group-hover:visible invisible absolute top-0 bg-black bg-opacity-20 w-full h-full z-20 text-center" + > + <h1 className="text-[12px] lg:text-sm pt-28 lg:pt-44 font-bold opacity-100"> + {checkProgress(progress)} + </h1> + </div> + )} + <div className="flex gap-1 text-[13px] lg:text-base"> + <h1>Episode {anime.nextAiringEpisode.episode} in</h1> + <h1 className="font-bold"> + {convertSecondsToTime( + anime?.nextAiringEpisode?.timeUntilAiring + )} + </h1> + </div> + </div> + </div> + )} <Image draggable={false} src={ @@ -72,17 +180,27 @@ export default function Content({ ids, section, data }) { anime.coverImage?.large || "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png" } - className="z-20 h-[192px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md" + className="z-20 h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md brightness-90" /> </Link> </div> ); })} + {filteredData.length >= 10 && section !== "Recommendations" && ( + <div key={section} className="flex "> + <div className="h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md border-secondary border-2 flex flex-col gap-2 items-center text-center justify-center text-[#6a6a6a]"> + <h1 className="whitespace-pre-wrap text-sm"> + More on {section} + </h1> + <ArrowRightCircleIcon className="w-5 h-5" /> + </div> + </div> + )} </div> <MdChevronRight onClick={slideRight} size={30} - className={`mb-5 cursor-pointer hover:text-action absolute right-0 bg-gradient-to-l from-[#141519] z-40 h-full hover:opacity-100 hover:bg-gradient-to-l ${ + className={`hidden md:block mb-5 cursor-pointer hover:text-action absolute right-0 bg-gradient-to-l from-[#141519] z-40 h-full hover:opacity-100 hover:bg-gradient-to-l ${ scrollRight ? "visible" : "hidden" }`} /> @@ -90,3 +208,37 @@ export default function Content({ ids, section, data }) { </div> ); } + +function convertSecondsToTime(sec) { + let days = Math.floor(sec / (3600 * 24)); + let hours = Math.floor((sec % (3600 * 24)) / 3600); + let minutes = Math.floor((sec % 3600) / 60); + + let time = ""; + + if (days > 0) { + time += `${days}d `; + time += `${hours}h`; + } else { + time += `${hours}h `; + time += `${minutes}m`; + } + + return time.trim(); +} + +function checkProgress(entry) { + const { progress, media } = entry; + const { episodes, nextAiringEpisode } = media; + + if (nextAiringEpisode !== null) { + const { episode } = nextAiringEpisode; + + if (episode - progress > 1) { + const missedEpisodes = episode - progress - 1; + return `${missedEpisodes} episode${missedEpisodes > 1 ? "s" : ""} behind`; + } + } + + return; +} diff --git a/components/hero/genres.js b/components/hero/genres.js new file mode 100644 index 0000000..1c8a475 --- /dev/null +++ b/components/hero/genres.js @@ -0,0 +1,69 @@ +import Image from "next/image"; +import { ChevronRightIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; + +const g = [ + { + name: "Action", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx20958-HuFJyr54Mmir.jpg", + }, + { + name: "Comedy", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx21202-TfzXuWQf2oLQ.png", + }, + { + name: "Horror", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx127230-FlochcFsyoF4.png", + }, + { + name: "Romance", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx124080-h8EPH92nyRfS.jpg", + }, + { + name: "Music", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx130003-5Y8rYzg982sq.png", + }, + { + name: "Sports", + img: "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx20464-eW7ZDBOcn74a.png", + }, +]; + +export default function Genres() { + return ( + <div className="antialiased"> + <div className="flex items-center justify-between lg:justify-normal lg:gap-3 px-5"> + <h1 className="font-karla text-[20px] font-bold">Top Genres</h1> + <ChevronRightIcon className="w-5 h-5" /> + </div> + <div className="flex xl:justify-center items-center relative"> + <div className="bg-gradient-to-r from-primary to-transparent z-40 absolute w-7 h-[300px] left-0" /> + <div className="flex lg:gap-8 gap-3 lg:p-10 py-8 px-5 z-30 overflow-y-hidden overflow-x-scroll snap-x snap-proximity scrollbar-none relative"> + <div className="flex lg:gap-10 gap-3"> + {g.map((a, index) => ( + <Link + href={`/search/anime/?genres=${a.name}`} + key={index} + className="relative hover:shadow-lg hover:scale-105 duration-200 cursor-pointer ease-out h-[190px] w-[135px] lg:h-[265px] lg:w-[230px] rounded-md shrink-0" + > + <div className="bg-gradient-to-b from-transparent to-[#0c0d10] h-[190px] w-[135px] lg:h-[265px] lg:w-[230px] rounded-md absolute flex justify-center items-end"> + <h1 className="pb-7 lg:text-xl font-karla font-semibold"> + {a.name} + </h1> + </div> + <Image + src={a.img} + alt="genres images" + width={1000} + height={1000} + className="object-cover shrink-0 h-[190px] w-[135px] lg:h-[265px] lg:w-[230px] rounded-md" + /> + </Link> + ))} + </div> + </div> + <div className="bg-gradient-to-l from-primary to-transparent z-40 absolute w-7 h-[300px] right-0" /> + </div> + </div> + ); +} diff --git a/components/searchBar.js b/components/searchBar.js new file mode 100644 index 0000000..35e9b45 --- /dev/null +++ b/components/searchBar.js @@ -0,0 +1,144 @@ +import { useState, useEffect, useRef } from "react"; +import { motion as m, AnimatePresence } from "framer-motion"; +import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import { useAniList } from "../lib/useAnilist"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/router"; + +const SearchBar = () => { + const [isOpen, setIsOpen] = useState(false); + const searchBoxRef = useRef(null); + + const router = useRouter(); + + const { aniAdvanceSearch } = useAniList(); + const [data, setData] = useState(null); + const [query, setQuery] = useState(""); + + useEffect(() => { + if (isOpen) { + searchBoxRef.current.querySelector("input").focus(); + } + const handleKeyDown = (e) => { + if (e.ctrlKey && e.code === "Space") { + setIsOpen((prev) => !prev); + setData(null); + setQuery(""); + } + }; + + document.addEventListener("keydown", handleKeyDown); + + const handleClick = (e) => { + if (searchBoxRef.current && !searchBoxRef.current.contains(e.target)) { + setIsOpen(false); + } + }; + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("click", handleClick); + }; + }, [isOpen]); + + async function search() { + const data = await aniAdvanceSearch({ + search: query, + type: "ANIME", + perPage: 10, + }); + setData(data); + } + + useEffect(() => { + if (query) { + search(); + } + }, [query]); + + function handleSubmit(e) { + e.preventDefault(); + if (data?.media.length) { + router.push(`/anime/${data?.media[0].id}`); + } + } + + return ( + <AnimatePresence> + {isOpen && ( + <m.div + initial={{ opacity: 0, y: -100 }} + animate={{ opacity: 1, y: 0 }} + exit={{ opacity: 0, y: -100 }} + className="fixed top-0 w-screen flex justify-center z-50" + > + <div + ref={searchBoxRef} + className={` bg-[#1c1c1fef] text-white p-4 ${ + isOpen ? "flex" : "hidden" + } flex-col w-[80%] backdrop-blur-sm rounded-b-lg`} + > + <form onSubmit={handleSubmit}> + <input + type="text" + className="w-full rounded-lg px-4 py-2 mb-2 bg-[#474747]" + placeholder="Search..." + onChange={(e) => setQuery(e.target.value)} + /> + </form> + <div className="flex flex-col gap-2 p-2 font-karla"> + {data?.media.map((i) => ( + <Link + key={i.id} + href={i.type === "ANIME" ? `/anime/${i.id}` : `/`} + className="flex hover:bg-[#3e3e3e] rounded-md" + > + <Image + src={i.coverImage.extraLarge} + alt="search results" + width={500} + height={500} + className="object-cover w-14 h-14 rounded-md" + /> + <div className="flex items-center justify-between w-full px-5"> + <div> + <h1>{i.title.userPreferred}</h1> + <h5 className="text-sm font-light text-[#878787] flex gap-2"> + {i.status + ?.toLowerCase() + .replace(/^\w/, (c) => c.toUpperCase())}{" "} + {i.status && i.season && <>·</>}{" "} + {i.season + ?.toLowerCase() + .replace(/^\w/, (c) => c.toUpperCase())}{" "} + {(i.status || i.season) && i.episodes && <>·</>}{" "} + {i.episodes || 0} Episodes + </h5> + </div> + <div className="text-sm text-[#b5b5b5] "> + <h1> + {i.type + ?.toLowerCase() + .replace(/^\w/, (c) => c.toUpperCase())} + </h1> + </div> + </div> + </Link> + ))} + </div> + {query && ( + <button className="flex items-center gap-2 justify-center"> + <MagnifyingGlassIcon className="h-5 w-5" /> + <Link href={`/search/${query}`}>More Results...</Link> + </button> + )} + </div> + </m.div> + )} + </AnimatePresence> + ); +}; + +export default SearchBar; diff --git a/components/videoPlayer.js b/components/videoPlayer.js index c441acc..8594645 100644 --- a/components/videoPlayer.js +++ b/components/videoPlayer.js @@ -16,7 +16,6 @@ export default function VideoPlayer({ }) { const [url, setUrl] = useState(); const [source, setSource] = useState([]); - const [loading, setLoading] = useState(true); const { markProgress } = useAniList(session); useEffect(() => { @@ -52,7 +51,6 @@ export default function VideoPlayer({ setUrl(defUrl); setSource(source); - setLoading(false); } catch (error) { console.error(error); } diff --git a/lib/useAnilist.js b/lib/useAnilist.js index 6a0a986..fc25902 100644 --- a/lib/useAnilist.js +++ b/lib/useAnilist.js @@ -20,6 +20,10 @@ export function useAniList(session, stats) { score media { id + nextAiringEpisode { + timeUntilAiring + episode + } title { english romaji @@ -104,34 +108,7 @@ export function useAniList(session, stats) { "Content-Type": "application/json", }, body: JSON.stringify({ - query: ` - query ($username: String, $status: MediaListStatus) { - MediaListCollection(userName: $username, type: ANIME, status: $status) { - lists { - status - name - entries { - id - mediaId - status - progress - score - media { - id - title { - english - romaji - } - episodes - coverImage { - large - } - } - } - } - } - } - `, + query: queryMedia, variables: { username: username, status: statuss?.stats, diff --git a/package-lock.json b/package-lock.json index 5ecc78b..bdb2ab2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "moopa", - "version": "3.5.0", + "version": "3.5.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "moopa", - "version": "3.5.0", + "version": "3.5.7", "dependencies": { "@apollo/client": "^3.7.3", "@heroicons/react": "^2.0.17", "@next/font": "13.0.7", + "@vercel/og": "^0.5.4", "artplayer": "latest", "axios": "^1.2.2", "closest-match": "^1.3.3", @@ -3259,6 +3260,14 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/@resvg/resvg-wasm": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.4.1.tgz", + "integrity": "sha512-yi6R0HyHtsoWTRA06Col4WoDs7SvlXU3DLMNP2bdAgs7HK18dTEVl1weXgxRzi8gwLteGUbIg29zulxIB3GSdg==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3339,6 +3348,21 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "dev": true }, + "node_modules/@shuding/opentype.js": { + "version": "1.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz", + "integrity": "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==", + "dependencies": { + "fflate": "^0.7.3", + "string.prototype.codepointat": "^0.2.1" + }, + "bin": { + "ot": "bin/ot" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -3547,6 +3571,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vercel/og": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.5.4.tgz", + "integrity": "sha512-o4Zjgw66HPtfGFYG4CFcKfnCcK6qCY+3FC6g22Z1SdAN5jN8XjbHCh3hRVHH5IGBQp8mxH941EcH53KimAm+wg==", + "dependencies": { + "@resvg/resvg-wasm": "2.4.1", + "satori": "0.7.2", + "yoga-wasm-web": "0.3.3" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", @@ -4289,6 +4326,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001481", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", @@ -4496,6 +4541,34 @@ "node": ">=8" } }, + "node_modules/css-background-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz", + "integrity": "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==" + }, + "node_modules/css-box-shadow": { + "version": "1.0.0-3", + "resolved": "https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz", + "integrity": "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==" + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -5381,6 +5454,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -5866,6 +5944,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hex-rgb": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz", + "integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", @@ -6551,6 +6640,23 @@ "node": ">=10" } }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7231,6 +7337,11 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7243,6 +7354,15 @@ "node": ">=6" } }, + "node_modules/parse-css-color": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz", + "integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==", + "dependencies": { + "color-name": "^1.1.4", + "hex-rgb": "^4.1.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7496,8 +7616,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/preact": { "version": "10.13.2", @@ -7944,6 +8063,30 @@ "node": ">=6" } }, + "node_modules/satori": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/satori/-/satori-0.7.2.tgz", + "integrity": "sha512-Eltg0/i3OEbBLaveCnYi2j+p0J9Bb5rmDMXddq4Zy0/NYHbpTkPIlPYGgb+DKamhmvXhiGvWGiFdqHVjJCaCpA==", + "dependencies": { + "@shuding/opentype.js": "1.4.0-beta.0", + "css-background-parser": "^0.1.0", + "css-box-shadow": "1.0.0-3", + "css-to-react-native": "^3.0.0", + "emoji-regex": "^10.2.1", + "linebreak": "^1.1.0", + "parse-css-color": "^0.2.1", + "postcss-value-parser": "^4.2.0", + "yoga-wasm-web": "^0.3.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/satori/node_modules/emoji-regex": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", + "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -8131,6 +8274,11 @@ "node": ">= 0.4" } }, + "node_modules/string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -8582,6 +8730,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -8780,6 +8933,15 @@ "node": ">=4" } }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -9364,6 +9526,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoga-wasm-web": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz", + "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==" + }, "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", diff --git a/package.json b/package.json index 4c718b2..1106b7e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moopa", - "version": "3.5.5", + "version": "3.5.7", "private": true, "scripts": { "dev": "next dev", @@ -13,6 +13,7 @@ "@apollo/client": "^3.7.3", "@heroicons/react": "^2.0.17", "@next/font": "13.0.7", + "@vercel/og": "^0.5.4", "artplayer": "latest", "axios": "^1.2.2", "closest-match": "^1.3.3", diff --git a/pages/anime/[...id].js b/pages/anime/[...id].js index ae6ac34..dc385f9 100644 --- a/pages/anime/[...id].js +++ b/pages/anime/[...id].js @@ -140,16 +140,18 @@ const infoQuery = `query ($id: Int) { } }`; -export default function Info() { - const { data: session, status } = useSession(); +export default function Info({ info, color }) { + const { data: session } = useSession(); const [data, setData] = useState(null); - const [info, setInfo] = useState(null); + // const [infos, setInfo] = useState(null); const [episode, setEpisode] = useState(null); const [loading, setLoading] = useState(false); const [progress, setProgress] = useState(0); const [statuses, setStatuses] = useState(null); const [stall, setStall] = useState(false); - const [color, setColor] = useState(null); + const [domainUrl, setDomainUrl] = useState(""); + + // console.log(info); const [showAll, setShowAll] = useState(false); const [open, setOpen] = useState(false); @@ -164,13 +166,15 @@ export default function Info() { (data) => data.mediaRecommendation ); - // const [log, setLog] = useState(null); - // console.log(rec); - useEffect(() => { + const { protocol, host } = window.location; + const url = `${protocol}//${host}`; + + setDomainUrl(url); + const defaultState = { data: null, - info: null, + // info: null, episode: null, loading: true, statuses: null, @@ -204,23 +208,23 @@ export default function Info() { if (id) { setLoading(false); try { - const [res, info] = await Promise.all([ + const [res] = await Promise.all([ fetch(`https://api.moopa.my.id/meta/anilist/info/${id?.[0]}`), - fetch("https://graphql.anilist.co/", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: infoQuery, - variables: { - id: id?.[0], - }, - }), - }), + // fetch("https://graphql.anilist.co/", { + // method: "POST", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify({ + // query: infoQuery, + // variables: { + // id: id?.[0], + // }, + // }), + // }), ]); const data = await res.json(); - const infos = await info.json(); + // const infos = await info.json(); if (res.status === 500) { setEpisode(null); @@ -229,10 +233,10 @@ export default function Info() { } else if (res.status === 404) { window.location.href("/404"); } - setInfo(infos.data.Media); + // setInfo(infos.data.Media); // setLog(data); - const textColor = setTxtColor(infos.data.Media.coverImage?.color); + // const textColor = setTxtColor(infos.data.Media.coverImage?.color); if (!data || data?.episodes?.length === 0) { const res = await fetch( @@ -246,19 +250,19 @@ export default function Info() { } else { setEpisode(datas.episodes); } - setColor({ - backgroundColor: `${data?.color || "#ffff"}`, - color: textColor, - }); + // setColor({ + // backgroundColor: `${data?.color || "#ffff"}`, + // color: textColor, + // }); setStall(true); } else { setEpisode(data.episodes); } - setColor({ - backgroundColor: `${data?.color || "#ffff"}`, - color: textColor, - }); + // setColor({ + // backgroundColor: `${data?.color || "#ffff"}`, + // color: textColor, + // }); if (session?.user?.name) { const response = await fetch("https://graphql.anilist.co/", { @@ -339,6 +343,21 @@ export default function Info() { ? info?.title?.romaji || info?.title?.english : "Retrieving Data..."} </title> + <meta name="twitter:card" content="summary_large_image" /> + <meta + name="twitter:title" + content={`Moopa - ${info.title.romaji || info.title.english}`} + /> + <meta + name="twitter:description" + content={`${info.description?.slice(0, 180)}...`} + /> + <meta + name="twitter:image" + content={`${domainUrl}/api/og?title=${ + info.title.romaji || info.title.english + }&image=${info.bannerImage || info.coverImage.extraLarge}`} + /> </Head> <Modal open={open} onClose={() => handleClose()}> <div> @@ -743,13 +762,15 @@ export default function Info() { // Something went wrong, can't retrieve any episodes :/ // </p> <div className="flex flex-col"> - <h1>{epiStatus} while retrieving data</h1> + {/* <h1>{epiStatus} while retrieving data</h1> */} <pre className={`rounded-md ${getLanguageClassName( "bash" )}`} > - <code>{error}</code> + <code> + Something went wrong while retrieving data :/ + </code> </pre> </div> )} @@ -785,6 +806,46 @@ export default function Info() { ); } +export async function getServerSideProps(context) { + const { id } = context.query; + + const res = await fetch("https://graphql.anilist.co/", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: infoQuery, + variables: { + id: id?.[0], + }, + }), + }); + + const json = await res.json(); + const data = json?.data?.Media; + + if (!data) { + return { + notFound: true, + }; + } + + const textColor = setTxtColor(data?.coverImage?.color); + + const color = { + backgroundColor: `${data?.coverImage?.color || "#ffff"}`, + color: textColor, + }; + + return { + props: { + info: data, + color: color, + }, + }; +} + function convertSecondsToTime(sec) { let days = Math.floor(sec / (3600 * 24)); let hours = Math.floor((sec % (3600 * 24)) / 3600); diff --git a/pages/api/og.jsx b/pages/api/og.jsx new file mode 100644 index 0000000..b1cf238 --- /dev/null +++ b/pages/api/og.jsx @@ -0,0 +1,103 @@ +import { ImageResponse } from "@vercel/og"; + +export const config = { + runtime: "edge", +}; + +const karla = fetch( + new URL("../../assets/Karla-MediumItalic.ttf", import.meta.url) +).then((res) => res.arrayBuffer()); +const outfit = fetch( + new URL("../../assets/Outfit-Regular.ttf", import.meta.url) +).then((res) => res.arrayBuffer()); + +export default async function handler(request) { + const Karla = await karla; + const Outfit = await outfit; + + const { searchParams } = request.nextUrl; + const hasTitle = searchParams.has("title"); + const title = hasTitle + ? searchParams.get("title").length > 64 + ? searchParams.get("title").slice(0, 64) + "..." + : searchParams.get("title") + : "Watch Now"; + const image = searchParams.get("image"); + if (!title) { + return new ImageResponse(<>Visit with "?username=vercel"</>, { + width: 1200, + height: 630, + }); + } + + return new ImageResponse( + ( + <div + style={{ + display: "flex", + fontSize: 60, + color: "black", + background: "#f6f6f6", + width: "100%", + height: "100%", + backgroundImage: `url(${image})`, + }} + className="relative w-[1900px] h-[400px] text-[10px]" + > + <div + className="w-[1900px] h-[400px]" + style={{ + display: "flex", + width: "100%", + paddingLeft: 100, + alignItems: "center", + color: "white", + position: "relative", + backgroundImage: `linear-gradient(to right, rgba(0,0,0,0.93), rgba(0,0,0,0.8) , rgba(0,0,0,0.2))`, + filter: "brightness(20%)", + }} + > + <span + style={{ + display: "flex", + position: "absolute", + top: 10, + left: 25, + fontSize: "40", + color: "#FF7F57", + fontFamily: "Outfit", + filter: "brightness(100%)", + }} + > + moopa + </span> + <h1 + style={{ + width: "70%", + fontSize: "70px", + filter: "brightness(100%)", + }} + > + {title} + </h1> + </div> + </div> + ), + { + width: 1900, + height: 400, + fonts: [ + { + name: "Karla", + data: Karla, + style: "normal", + }, + { + name: "Outfit", + data: Outfit, + style: "normal", + }, + ], + } + ); +} diff --git a/pages/index.js b/pages/index.js index 88687d7..abe5df3 100644 --- a/pages/index.js +++ b/pages/index.js @@ -13,6 +13,8 @@ import { useSession, signIn, signOut } from "next-auth/react"; import { useAniList } from "../lib/useAnilist"; import { getServerSession } from "next-auth/next"; import { authOptions } from "./api/auth/[...nextauth]"; +import SearchBar from "../components/searchBar"; +import Genres from "../components/hero/genres"; export function Navigasi() { const { data: sessions, status } = useSession(); @@ -38,7 +40,7 @@ export function Navigasi() { <div className="flex items-center lg:gap-16 lg:pt-7"> <Link href="/" - className=" font-outfit text-[40px] font-bold text-[#FF7F57]" + className=" font-outfit lg:text-[40px] text-[30px] font-bold text-[#FF7F57]" > moopa </Link> @@ -78,7 +80,7 @@ export function Navigasi() { )} </ul> </div> - <div className="relative flex scale-75 items-center mb-7 lg:mb-0"> + <div className="relative flex lg:scale-75 scale-[65%] items-center mb-7 lg:mb-0"> <div className="search-box "> <input className="search-text" @@ -105,6 +107,10 @@ export default function Home({ detail, populars, sessions }) { const [list, setList] = useState(null); const [planned, setPlanned] = useState(null); const [greeting, setGreeting] = useState(""); + const [onGoing, setOnGoing] = useState(null); + + const [prog, setProg] = useState(null); + const popular = populars?.data; const data = detail.data[0]; @@ -140,10 +146,19 @@ export default function Home({ detail, populars, sessions }) { .map(({ media }) => media) .filter((media) => media); + const prog = getMedia?.entries.filter( + (item) => item.media.nextAiringEpisode !== null + ); + + setProg(prog); + const planned = plan?.[0]?.entries .map(({ media }) => media) .filter((media) => media); + const onGoing = list?.filter((item) => item.nextAiringEpisode !== null); + setOnGoing(onGoing); + if (list) { setList(list.reverse()); } @@ -154,6 +169,8 @@ export default function Home({ detail, populars, sessions }) { userData(); }, [sessions, current, plan]); + // console.log(log); + return ( <> <Head> @@ -361,6 +378,7 @@ export default function Home({ detail, populars, sessions }) { <div className="h-auto w-screen bg-[#141519] text-[#dbdcdd] "> <Navigasi /> + <SearchBar /> {/* PC / TABLET */} <div className=" hidden justify-center lg:flex my-16"> <div className="relative grid grid-rows-2 items-center lg:flex lg:h-[467px] lg:w-[80%] lg:justify-between"> @@ -402,9 +420,14 @@ export default function Home({ detail, populars, sessions }) { </div> </div> </div> + {/* {!sessions && ( + <h1 className="font-bold font-karla mx-5 text-[32px] mt-2 lg:mx-24 xl:mx-36"> + {greeting}! + </h1> + )} */} {sessions && ( - <div className="flex items-center mx-3 lg:mx-0 mt-10 lg:mt-0"> - <div className="lg:text-4xl lg:mx-32 flex items-center gap-3 text-2xl font-bold font-karla"> + <div className="flex items-center justify-center lg:bg-none mt-4 lg:mt-0 w-screen"> + <div className="lg:w-[85%] w-screen px-5 lg:px-0 lg:text-4xl flex items-center gap-3 text-2xl font-bold font-karla"> {greeting},<h1 className="lg:hidden">{sessions?.user.name}</h1> <button onClick={() => signOut()} @@ -419,13 +442,30 @@ export default function Home({ detail, populars, sessions }) { </div> )} - <div className="lg:mt-16 mt-12 flex flex-col items-center"> + <div className="lg:mt-16 mt-5 flex flex-col items-center"> <motion.div className="w-screen flex-none lg:w-[87%]" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5, staggerChildren: 0.2 }} // Add staggerChildren prop > + {sessions && onGoing && ( + <motion.div // Add motion.div to each child component + key="onGoing" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Content + ids="onGoing" + section="On-Going Anime" + data={onGoing} + og={prog} + /> + </motion.div> + )} + {sessions && list && ( <motion.div // Add motion.div to each child component key="listAnime" @@ -492,6 +532,16 @@ export default function Home({ detail, populars, sessions }) { /> </motion.div> )} + + <motion.div // Add motion.div to each child component + key="Genres" + initial={{ y: 20, opacity: 0 }} + whileInView={{ y: 0, opacity: 1 }} + transition={{ duration: 0.5 }} + viewport={{ once: true }} + > + <Genres /> + </motion.div> </motion.div> </div> </div> diff --git a/pages/search/[param].js b/pages/search/[param].js index 9fc2b17..96d6671 100644 --- a/pages/search/[param].js +++ b/pages/search/[param].js @@ -56,8 +56,11 @@ export default function Card() { let tipe = "ANIME"; let s = undefined; let y = NaN; + let gr = undefined; const query = router.query; + gr = query.genres; + if (query.param !== "anime" && query.param !== "manga") { hasil = query.param; } else if (query.param === "anime") { @@ -96,9 +99,8 @@ export default function Card() { const [search, setQuery] = useState(hasil); const [type, setSelectedType] = useState(tipe); - const [genres, setSelectedGenre] = useState(); + // const [genres, setSelectedGenre] = useState(); const [sort, setSelectedSort] = useState(); - // console.log(data); const [isVisible, setIsVisible] = useState(false); @@ -112,7 +114,7 @@ export default function Card() { const data = await aniAdvanceSearch({ search: search, type: type, - genres: genres, + genres: gr, page: page, sort: sort, season: s, @@ -137,7 +139,7 @@ export default function Card() { setPage(1); setNextPage(true); advance(); - }, [search, type, genres, sort, s, y]); + }, [search, type, sort, s, y, gr]); useEffect(() => { advance(); @@ -178,8 +180,9 @@ export default function Card() { function trash() { setQuery(null); inputRef.current.value = ""; - setSelectedGenre(null); + // setSelectedGenre(null); setSelectedSort(["POPULARITY_DESC"]); + router.push(`/search/${tipe.toLocaleLowerCase()}`); } function handleVisible() { @@ -202,7 +205,7 @@ export default function Card() { <div className="bg-primary"> <Navbar /> <div className="min-h-screen mt-10 mb-14 text-white items-center gap-5 xl:gap-0 flex flex-col"> - <div className="w-screen px-10 xl:w-[80%] xl:h-[10rem] flex text-center xl:items-end xl:pb-16 justify-center lg:gap-7 xl:gap-10 gap-3 font-karla font-light"> + <div className="w-screen px-10 xl:w-[80%] xl:h-[10rem] flex text-center xl:items-end xl:pb-10 justify-center lg:gap-7 xl:gap-10 gap-3 font-karla font-light"> <div className="text-start"> <h1 className="font-bold xl:pb-5 pb-3 hidden lg:block text-md pl-1 font-outfit"> TITLE @@ -299,6 +302,7 @@ export default function Card() { </div> </div> </div> + <div className="w-screen xl:w-[64%] flex xl:justify-end xl:pl-0"> <AnimatePresence> {isVisible && ( @@ -309,19 +313,24 @@ export default function Card() { exit={{ opacity: 0, y: -10 }} className="xl:pb-16" > - <div className="text-start items-center xl:items-start flex w-screen xl:w-auto px-8 xl:px-0 flex-row justify-between xl:flex-col pb-5 "> + <div className="text-start items-center xl:items-start flex w-screen xl:w-auto px-8 xl:px-0 flex-row justify-between xl:flex-col pb-5 lg:pb-0 "> <h1 className="font-bold xl:pb-5 text-md pl-1 font-outfit"> GENRE </h1> <select className="w-[195px] xl:w-[297px] xl:h-[46px] h-[35px] bg-secondary rounded-[10px] flex items-center text-center cursor-pointer hover:bg-[#272b35] transition-all duration-300" - onChange={(e) => - setSelectedGenre( - e.target.value === "undefined" - ? undefined - : e.target.value - ) - } + onChange={(e) => { + // setSelectedGenre( + // e.target.value === "undefined" + // ? undefined + // : e.target.value + // ); + router.push( + `/search/${tipe.toLocaleLowerCase()}/?genres=${ + e.target.value + }` + ); + }} > <option value="undefined">Select a Genre</option> {genre.map((option) => { @@ -372,6 +381,13 @@ export default function Card() { )} </AnimatePresence> </div> + {gr && ( + <div className="lg:w-[70%] px-5 lg:px-4 w-screen lg:mb-6"> + <h1 className="font-bold text-[25px] font-karla"> + Looking for : {gr} + </h1> + </div> + )} <div className="flex flex-col gap-14 items-center"> <AnimatePresence> <div |