diff options
Diffstat (limited to 'pages')
| -rw-r--r-- | pages/anime/[...id].js | 38 | ||||
| -rw-r--r-- | pages/anime/watch/[...info].js | 131 | ||||
| -rw-r--r-- | pages/api/getUser.js | 20 | ||||
| -rw-r--r-- | pages/api/update-user.js | 21 | ||||
| -rw-r--r-- | pages/dmca.js | 2 | ||||
| -rw-r--r-- | pages/index.js | 281 | ||||
| -rw-r--r-- | pages/search.js | 9 | ||||
| -rw-r--r-- | pages/test.js | 21 | ||||
| -rw-r--r-- | pages/testing.js | 34 |
9 files changed, 382 insertions, 175 deletions
diff --git a/pages/anime/[...id].js b/pages/anime/[...id].js index d42a394..c2e69e1 100644 --- a/pages/anime/[...id].js +++ b/pages/anime/[...id].js @@ -75,7 +75,9 @@ export default function Himitsu({ if (!Array.isArray(existingData)) { existingData = []; } - const index = existingData.findIndex((item) => item.title === props.title); + const index = existingData.findIndex( + (item) => item.title.romaji === props.title.romaji + ); if (index !== -1) { existingData.splice(index, 1); } @@ -176,13 +178,17 @@ export default function Himitsu({ href={`/anime/watch/${epi1[0].id}/${info.id}`} onClick={() => handleStore({ - title: - info.title?.english || - info.title.romaji || - info.title.native, + title: { + romaji: + info.title.romaji || + info.title.english || + info.title.native, + }, description: info.description, - image: info.image, - id: info.id, + coverImage: { + extraLarge: info.image, + }, + id: parseInt(info.id), }) } > @@ -358,7 +364,7 @@ export default function Himitsu({ className="object-cover h-full w-full shrink-0 rounded-l-md" /> </div> - <div className="min-w-[80%] h-full grid px-3 items-center"> + <div className="h-full grid px-3 items-center"> <div className="text-action font-outfit font-bold"> {relation.relationType} </div> @@ -415,13 +421,17 @@ export default function Himitsu({ <Link onClick={() => handleStore({ - title: - info.title?.english || - info.title.romaji || - info.title.native, + title: { + romaji: + info.title.romaji || + info.title.english || + info.title.native, + }, description: info.description, - image: info.image, - id: info.id, + coverImage: { + extraLarge: info.image, + }, + id: parseInt(info.id), }) } href={`/anime/watch/${episode.id}/${info.id}/${ diff --git a/pages/anime/watch/[...info].js b/pages/anime/watch/[...info].js index 73e04f5..ed1a50b 100644 --- a/pages/anime/watch/[...info].js +++ b/pages/anime/watch/[...info].js @@ -9,17 +9,59 @@ import Head from "next/head"; import { useEffect, useState } from "react"; import Modal from "../../../components/modal"; +import { useNotification } from "../../../lib/useNotify"; + +import { useSession, signIn, signOut } from "next-auth/react"; +import AniList from "../../../components/media/aniList"; + +import { AnimatePresence, motion as m } from "framer-motion"; +import Navbar from "../../../components/navbar"; +import { Navigasi } from "../.."; + export default function Info({ info }) { + const { data: session, status } = useSession(); const title = info.aniData.title.romaji || info.aniData.title.english; const data = info.aniData; const fallback = info.epiFallback; + const { Notification: NotificationComponent, show } = useNotification(); + + // console.log(session); + + const playingEpisode = data.episodes + .filter((item) => item.id == info.id) + .map((item) => item.number); const [open, setOpen] = useState(false); + const [aniStatus, setAniStatus] = useState(""); + const [aniProgress, setAniProgress] = useState(parseInt(playingEpisode)); + + const handleStatus = (e) => { + setAniStatus(e.target.value); + }; + + const handleProgress = (e) => { + const value = parseFloat(e.target.value); + if (!isNaN(value) && value >= 0 && value <= data.totalEpisodes) { + setAniProgress(value); + } + }; + + const handleSubmit = (e) => { + e.preventDefault(); + const formData = { status: aniStatus, progress: aniProgress }; + console.log(formData); + }; const playingTitle = data.episodes .filter((item) => item.id == info.id) .map((item) => item.title); + if (status === "loading") { + return <p>Loading...</p>; + } + + console.log(parseInt(playingEpisode)); + return ( <> <Head> @@ -27,29 +69,91 @@ export default function Info({ info }) { {fallback ? data.title.romaji || data.title.english : playingTitle} </title> </Head> + + <NotificationComponent /> + <Modal open={open} onClose={() => setOpen(false)}> - <div className="bg-[#202020] rounded-lg w-[268px] text-center"> + <div className="bg-[#202020] rounded-lg text-center"> <div className="p-5 grid gap-2 justify-center place-items-center"> <h1 className="text-md font-extrabold font-karla"> Save this Anime to Your List </h1> - <h1 className="text-sm font-karla font-extralight w-[205px]"> - Are you sure you want to save this anime to your list? - </h1> - <div className="flex gap-12 items-center pt-3 justify-between"> - <button className="p-2 font-karla font-extrabold text-sm bg-[#93FF3E] w-[84px] rounded-[10px] text-black shadow"> - YES - </button> + {!session && ( <button - onClick={() => setOpen(false)} - className="p-2 font-karla font-extrabold text-sm bg-white w-[84px] rounded-[10px] text-black shadow-lg" + className="flex items-center bg-[#3a3a3a] mt-4 rounded-md text-white p-1" + onClick={() => signIn("AniListProvider")} > - NO + <h1 className="px-1 font-bold font-karla"> + Login with AniList + </h1> + <div className="scale-[60%] pb-[1px]"> + <AniList /> + </div> </button> - </div> + )} + {session && ( + <> + <form + onSubmit={handleSubmit} + className="grid grid-cols-2 gap-5 max-w-sm mx-auto mt-5 items-center" + > + <div className="mb-4"> + <label + htmlFor="option" + className="block font-bold mb-2 text-sm" + > + Select an option + </label> + <select + id="option" + value={aniStatus} + onChange={handleStatus} + className="form-select block w-full px-2 py-1 rounded-lg shadow-sm focus:outline-none focus:shadow-outline-blue focus:border-blue-300" + > + {aniStatus === "" && ( + <option value="" hidden> + Select an option + </option> + )} + <option value="option1">Option 1</option> + <option value="option2">Option 2</option> + <option value="option3">Option 3</option> + </select> + </div> + <div className="mb-4"> + <label + htmlFor="number" + className="block text-sm font-bold mb-2" + > + Episode Progress + </label> + <input + id="number" + type="number" + step="1" + min="0" + max={data.totalEpisodes} + className="form-input block w-full px-2 py-1 rounded-lg shadow-sm focus:outline-none focus:shadow-outline-blue focus:border-blue-300" + value={aniProgress} + onChange={handleProgress} + /> + </div> + <div className="col-start-2 row-start-2 w-full justify-items-end text-center"> + <button + type="submit" + className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" + onClick={() => setOpen(false)} + > + Submit + </button> + </div> + </form> + </> + )} </div> </div> </Modal> + <Navigasi /> <div className="min-h-screen 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%]"> <div className="h-auto aspect-video z-20"> @@ -59,6 +163,9 @@ export default function Info({ info }) { seek={info.seek} titles={title} id={info.id} + progress={parseInt(playingEpisode)} + session={session} + aniId={parseInt(data.id)} /> </div> <div> diff --git a/pages/api/getUser.js b/pages/api/getUser.js new file mode 100644 index 0000000..7df10a6 --- /dev/null +++ b/pages/api/getUser.js @@ -0,0 +1,20 @@ +import clientPromise from "../../lib/mongodb"; + +export async function getUser(userName) { + const client = await clientPromise; + const db = client.db("authbase"); + + const collection = db.collection("users"); + const user = await collection.findOne({ name: userName }); + + user._id = String(user._id); + + return user; +} + +export default async function handler(req, res) { + const { userName } = req.query; + const user = await getUser(userName); + + res.status(200).json(user); +} diff --git a/pages/api/update-user.js b/pages/api/update-user.js new file mode 100644 index 0000000..9f652ac --- /dev/null +++ b/pages/api/update-user.js @@ -0,0 +1,21 @@ +// pages/api/update-user.js +import clientPromise from "../../lib/mongodb"; + +export default async function handler(req, res) { + const client = await clientPromise; + const db = client.db("authbase"); + const collection = db.collection("users"); + + const { name, newData } = req.body; // id is the user ID and newData is the new data you want to set + + try { + const result = await collection.updateOne( + { name: name }, + { $set: newData } + ); + + res.status(200).json(result); + } catch (error) { + res.status(500).json({ error: "Unable to update user data" }); + } +} diff --git a/pages/dmca.js b/pages/dmca.js index 7244bf7..c94781b 100644 --- a/pages/dmca.js +++ b/pages/dmca.js @@ -22,7 +22,7 @@ export default function DMCA() { <link rel="icon" href="/c.svg" /> </Head> <Layout> - <div className="min-h-screen flex w-screen justify-center items-center"> + <div className="min-h-screen z-20 flex w-screen justify-center items-center"> <div className="w-[75%] text-2xl gap-7 flex flex-col my-[10rem]"> <div className="flex"> <h1 className="text-4xl font-bold font-karla rounded-md bg-[#212121] p-3"> diff --git a/pages/index.js b/pages/index.js index 7c26f0b..d75cb0f 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,4 +1,3 @@ -import { MdChevronLeft, MdChevronRight } from "react-icons/md"; import { aniListData } from "../lib/AniList"; import React, { useState, useEffect } from "react"; import ReactHtmlParser from "kt-react-html-parser"; @@ -9,12 +8,90 @@ import Image from "next/image"; import Content from "../components/hero/content"; import { useRouter } from "next/router"; -export default function Home({ detail, populars, topDesc }) { +import { useSession, signIn } from "next-auth/react"; + +export function Navigasi() { + const { data: session, status } = useSession(); + + const router = useRouter(); + + const handleFormSubmission = (inputValue) => { + router.push(`/search?hasil=${encodeURIComponent(inputValue)}`); + }; + + const handleKeyDown = async (event) => { + if (event.key === "Enter") { + event.preventDefault(); + const inputValue = event.target.value; + handleFormSubmission(inputValue); + } + }; + return ( + <> + {/* NAVBAR PC */} + <div className="flex items-center justify-center"> + <div className="flex w-full items-center justify-between px-5 md:mx-[94px]"> + <div className="flex items-center md:gap-16 md:pt-7"> + <Link + href="/" + className=" font-outfit text-[40px] font-bold text-[#FF7F57]" + > + moopa + </Link> + <ul className="hidden items-center gap-10 pt-2 font-outfit text-[14px] md:flex"> + <Link href="/search"> + <li>AniList Index</li> + </Link> + <Link href="/test"> + <li>Manga</li> + </Link> + <li>Anime</li> + {status === "loading" && <li>Loading...</li>} + {!session && ( + <li> + <button + onClick={() => signIn("AniListProvider")} + className="ring-1 ring-action font-karla font-bold px-2 py-1 rounded-md" + > + Sign in + </button> + </li> + )} + {session && ( + <li className="text-center"> + {/* <div className="p-2"><img src={session?.user.image.large} alt="imagine" /></div> */} + My List + </li> + )} + </ul> + </div> + <div className="relative flex scale-75 items-center mb-7 md:mb-0"> + <div className="search-box "> + <input + className="search-text" + type="text" + placeholder="Search Anime" + onKeyDown={handleKeyDown} + /> + <a href="#" className="search-btn"> + <i className="fas fa-search"></i> + </a> + </div> + </div> + </div> + </div> + </> + ); +} + +export default function Home({ detail, populars }) { + const { data: session, status } = useSession(); const [isVisible, setIsVisible] = useState(false); const [recently, setRecently] = useState(null); - const popular = populars.data; + const popular = populars?.data; const data = detail.data[0]; - const router = useRouter(); + + const greeting = getGreeting(); const handleShowClick = () => { setIsVisible(true); @@ -34,32 +111,6 @@ export default function Home({ detail, populars, topDesc }) { fetchData(); }, []); - function handleRemove() { - localStorage.removeItem("recentWatch"); - setRecently(null); - } - - const slideLeft = () => { - var slider = document.getElementById("recentslider"); - slider.scrollLeft = slider.scrollLeft - 500; - }; - const slideRight = () => { - var slider = document.getElementById("recentslider"); - slider.scrollLeft = slider.scrollLeft + 500; - }; - - const handleFormSubmission = (inputValue) => { - router.push(`/search?hasil=${encodeURIComponent(inputValue)}`); - }; - - const handleKeyDown = async (event) => { - if (event.key === "Enter") { - event.preventDefault(); - const inputValue = event.target.value; - handleFormSubmission(inputValue); - } - }; - return ( <> <Head> @@ -80,6 +131,8 @@ export default function Home({ detail, populars, topDesc }) { /> <link rel="icon" href="/c.svg" /> </Head> + + {/* NAVBAR */} <div className="z-50"> {!isVisible && ( <button @@ -103,7 +156,38 @@ export default function Home({ detail, populars, topDesc }) { )} {/* Mobile Menu */} - <div> + <div className="md:hidden"> + {isVisible && ( + <button + type="button" + onClick={() => signIn("AniListProvider")} + className="fixed bottom-[100px] w-[60px] h-[60px] flex items-center justify-center right-[20px] rounded-full z-50 bg-[#101925]" + > + {!session && ( + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="32" + height="26" + fill="none" + viewBox="0 0 33 26" + > + <path + fill="#fff" + d="M15.167 24.638v-1.732h8.942c.209 0 .4-.087.573-.26.174-.174.26-.365.26-.573V3.28c0-.209-.086-.4-.26-.573-.173-.174-.364-.26-.573-.26h-8.942V.714h8.942c.707 0 1.311.25 1.813.753.502.502.753 1.106.753 1.813v18.792c0 .706-.25 1.31-.753 1.812a2.471 2.471 0 01-1.813.753h-8.942zm-1.532-6.536l-1.303-1.26 3.32-3.322H3.86v-1.732h11.766l-3.321-3.321 1.321-1.233 5.448 5.448-5.438 5.42z" + ></path> + </svg> + </div> + )} + {session && ( + <img + src={session?.user.image.large} + alt="user avatar" + className="object-cover" + /> + )} + </button> + )} {isVisible && ( <div className="fixed bottom-[25px] right-[15px] z-50 flex h-[66px] w-[255px] items-center justify-center gap-8 rounded-[10px] text-[11px] bg-[#101925] shadow-menu md:hidden"> <div className="flex gap-7"> @@ -227,47 +311,17 @@ export default function Home({ detail, populars, topDesc }) { )} </div> </div> - <div className="h-auto w-screen bg-[#141519] text-white"> - <div className="flex items-center justify-center"> - <div className="flex w-full items-center justify-between px-5 md:mx-[94px]"> - <div className="flex items-center md:gap-16 md:pt-7"> - <h1 className=" font-outfit text-[40px] font-bold text-[#FF7F57]"> - moopa - </h1> - <ul className="hidden gap-10 pt-2 font-outfit text-[14px] md:flex"> - <Link href="/search"> - <li>AniList Index</li> - </Link> - <Link href="/test"> - <li>Manga</li> - </Link> - <li>Anime</li> - </ul> - </div> - <div className="relative flex scale-75 items-center"> - <div className="search-box "> - <input - className="search-text" - type="text" - placeholder="Search Anime" - onKeyDown={handleKeyDown} - /> - <a href="#" className="search-btn"> - <i className="fas fa-search"></i> - </a> - </div> - </div> - </div> - </div> + <div className="h-auto w-screen bg-[#141519] text-[#dbdcdd] "> + <Navigasi /> {/* PC / TABLET */} - <div className="mt-10 hidden justify-center lg:flex"> - <div className="relative grid grid-rows-2 items-center md:flex md:h-[760px] md:w-[80%] md:justify-between"> + <div className=" hidden justify-center lg:flex my-16"> + <div className="relative grid grid-rows-2 items-center md:flex md:h-[467px] md:w-[80%] md:justify-between"> <div className="row-start-2 flex h-full flex-col gap-7 md:w-[55%] md:justify-center"> - <h1 className="w-[85%] font-outfit font-extrabold md:text-[45px] line-clamp-2"> + <h1 className="w-[85%] font-outfit font-extrabold md:text-[34px] line-clamp-2"> {data.title.english || data.title.romaji || data.title.native} </h1> - <div className="font-roboto font-light md:text-[24px] line-clamp-5"> + <div className="font-roboto font-light md:text-[18px] line-clamp-5"> {ReactHtmlParser(data.description)} </div> @@ -277,84 +331,48 @@ export default function Home({ detail, populars, topDesc }) { legacyBehavior className="flex" > - <a className="rounded-sm p-3 font-karla font-light ring-1 ring-[#FF7F57]"> + <a className="rounded-sm p-3 text-md font-karla font-light ring-1 ring-[#FF7F57]"> START WATCHING </a> </Link> </div> </div> <div className="z-10 row-start-1 flex justify-center "> - <div className="relative md:h-[662px] md:w-[460px] md:scale-100"> - <div className="absolute bg-gradient-to-t from-[#141519] to-transparent md:h-[662px] md:w-[460px]" /> + <div className="relative md:h-[467px] md:w-[322px] md:scale-100"> + <div className="absolute bg-gradient-to-t from-[#141519] to-transparent md:h-[467px] md:w-[322px]" /> <Image draggable={false} - src={data.coverImage.extraLarge} + src={data.coverImage?.extraLarge || data.image} alt={data.title.english || data.title.romaji} width={460} height={662} priority - className="rounded-tl-xl rounded-tr-xl object-cover bg-blend-overlay md:h-[662px] md:w-[460px] " + className="rounded-tl-xl rounded-tr-xl object-cover bg-blend-overlay md:h-[467,6px] md:w-[322px]" /> </div> </div> </div> </div> + {session && ( + <div className="w-screen flex md:justify-center mx-3 md:mx-0 mt-10 md:mt-0"> + <div className="md:w-[86%] md:text-3xl text-2xl font-bold font-karla"> + {greeting}, {session?.user.name} + </div> + </div> + )} + {/* Mobile */} - <div className="mt-16 flex flex-col items-center"> + <div className="md:mt-16 mt-12 flex flex-col items-center"> <div className="w-screen flex-none lg:w-[87%]"> {recently && ( - <div> - <div className="flex items-center gap-5 lg:gap-10"> - <h1 className="px-5 font-outfit text-[20px] font-extrabold lg:text-[27px]"> - Recently Watched - </h1> - <div - className="cursor-pointer font-outfit font-light text-[#8f8f8f]" - onClick={() => handleRemove()} - > - Clear all - </div> - </div> - <div className="relative z-10 flex items-center py-10 lg:gap-2"> - <MdChevronLeft - onClick={slideLeft} - size={40} - className="mb-5 cursor-pointer opacity-50 hover:opacity-100" - /> - <div - id="recentslider" - className="scroll flex h-full w-full items-center overflow-x-scroll scroll-smooth whitespace-nowrap overflow-y-hidden scrollbar-hide lg:gap-5" - > - {recently.map((anime, index) => { - const url = encodeURIComponent(anime.title); - - return ( - <Link - href={`/anime/${anime.id}`} - key={index} - className="shrink-0 " - > - <Image - src={anime.image} - alt={anime.title || "cover image"} - width={209} - height={300} - className="z-20 h-[230px] w-[168px] object-cover p-2 duration-300 ease-in-out hover:scale-105 lg:h-[290px] lg:w-[209px]" - /> - </Link> - ); - })} - </div> - <MdChevronRight - onClick={slideRight} - size={40} - className="mb-5 cursor-pointer opacity-50 hover:opacity-100" - /> - </div> - </div> + <Content + ids="recentlyWatched" + section="Recently Watched" + data={recently} + /> )} {detail && ( @@ -390,16 +408,27 @@ export async function getServerSideProps({ req, res }) { page: 1, }); const genreDetail = await aniListData({ sort: "TYPE", page: 1 }); - const newTrend = await trendingDetail.props; - const trends = newTrend.data[0]; - const topDesc = trends.description.slice(0, 350) + "..."; return { props: { - topDesc: topDesc, genre: genreDetail.props, detail: trendingDetail.props, populars: popularDetail.props, }, }; } + +function getGreeting() { + const time = new Date().getHours(); + let greeting = ""; + + if (time >= 5 && time < 12) { + greeting = "Good morning"; + } else if (time >= 12 && time < 18) { + greeting = "Good afternoon"; + } else { + greeting = "Good evening"; + } + + return greeting; +} diff --git a/pages/search.js b/pages/search.js index 87f9cdc..4bb32af 100644 --- a/pages/search.js +++ b/pages/search.js @@ -107,8 +107,6 @@ export default function Card() { advance(); }, [search, type, seasonYear, season, genres, perPage, sort]); - console.log(data); - // useEffect(() => { // async function fetchData() { // setLoading(true); @@ -421,10 +419,3 @@ export default function Card() { </> ); } - -{ - /* <div className=" w-[228px]"> - <div className="bg-[#3B3C41] h-[313px] rounded-[10px]" /> - <h1 className="font-outfit font-extrabold text-[24px]">Anime Title</h1> - </div> */ -} diff --git a/pages/test.js b/pages/test.js index 57db350..0db3a17 100644 --- a/pages/test.js +++ b/pages/test.js @@ -13,7 +13,7 @@ import { useAniList } from "../lib/useAnilist"; export default function AniTest() { const { data: session, status } = useSession(); - const { media, aniAdvanceSearch } = useAniList(session); + const { media, aniAdvanceSearch, markComplete } = useAniList(session); const [advanceSearch, setAdvanceSearch] = useState(); const [search, setSearch] = useState(); @@ -53,6 +53,11 @@ export default function AniTest() { // const [open, setOpen] = useState(false); + async function markAsComplete(id) { + const response = await markComplete(id); + console.log(response); + } + async function advance() { const data = await aniAdvanceSearch( search, @@ -80,7 +85,7 @@ export default function AniTest() { // search: "naruto", // }); - console.log(advanceSearch); + // console.log(advanceSearch); return ( // <div className="h-[720px] w-[1280px]"> @@ -124,9 +129,6 @@ export default function AniTest() { /> </div> )} - {advanceSearch?.media.map((item) => { - return <div key={item.id}>{item.title.userPreferred}</div>; - })} {media?.length > 0 && ( <div className="flex-center flex-col gap-5"> {media.map((item, index) => { @@ -156,14 +158,7 @@ export default function AniTest() { </h3> {item.name === "Watching" && ( <button - onClick={() => - handleUpdateMediaEntry( - items.media.id, - "COMPLETED", - items.media.episodes, - 8 - ) - } + onClick={() => markAsComplete(items.media.id)} > Mark as Complete </button> diff --git a/pages/testing.js b/pages/testing.js new file mode 100644 index 0000000..ffa8080 --- /dev/null +++ b/pages/testing.js @@ -0,0 +1,34 @@ +import { getUser } from "./api/getUser"; + +export default function Testing({ user }) { + async function handleUpdate() { + const res = await fetch("/api/update-user", { + method: "POST", + body: JSON.stringify({ + name: "Factiven", + newData: { + settings: { + tracking: false, + }, + }, + }), + headers: { + "Content-Type": "application/json", + }, + }); + console.log(res.status); + } + + console.log(user.settings); + return <button onClick={() => handleUpdate()}>Click for update</button>; +} + +export async function getServerSideProps(context) { + const user = await getUser("Factiven"); + console.log(user); + return { + props: { + user: user, + }, + }; +} |