aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-05-16 22:40:02 +0700
committerGitHub <[email protected]>2023-05-16 22:40:02 +0700
commit9a5754fdba9d778f820fe89b44d1e21ca9f0bb4d (patch)
tree8bd574163e760216bc91f7b3c164232b6982efe8 /pages
parentUpdate v3.5.6 (diff)
downloadmoopa-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
Diffstat (limited to 'pages')
-rw-r--r--pages/anime/[...id].js129
-rw-r--r--pages/api/og.jsx103
-rw-r--r--pages/index.js60
-rw-r--r--pages/search/[param].js44
4 files changed, 283 insertions, 53 deletions
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 &quot;?username=vercel&quot;</>, {
+ 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