diff options
| author | real-zephex <[email protected]> | 2024-03-25 11:33:09 +0530 |
|---|---|---|
| committer | real-zephex <[email protected]> | 2024-03-25 11:33:09 +0530 |
| commit | 9202a2b6790e57dc35d0563d014e89d981a65e37 (patch) | |
| tree | f2258b96400d6df9753c2c52531ba7f8897b9fa3 /src | |
| parent | fix: requests are revalidated after 30 mins. this prevents stale data from be... (diff) | |
| download | dramalama-9202a2b6790e57dc35d0563d014e89d981a65e37.tar.xz dramalama-9202a2b6790e57dc35d0563d014e89d981a65e37.zip | |
feature added: mangas are now available
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/globals.css | 1 | ||||
| -rw-r--r-- | src/app/header/header.jsx | 11 | ||||
| -rw-r--r-- | src/app/manga/Manga.jsx | 63 | ||||
| -rw-r--r-- | src/app/manga/[title]/[id]/[read]/page.jsx | 51 | ||||
| -rw-r--r-- | src/app/manga/[title]/[id]/[read]/read.module.css | 43 | ||||
| -rw-r--r-- | src/app/manga/[title]/[id]/buttons.jsx | 28 | ||||
| -rw-r--r-- | src/app/manga/[title]/[id]/info.module.css | 135 | ||||
| -rw-r--r-- | src/app/manga/[title]/[id]/page.jsx | 102 | ||||
| -rw-r--r-- | src/app/manga/[title]/page.jsx | 74 | ||||
| -rw-r--r-- | src/app/manga/[title]/title.module.css | 61 | ||||
| -rw-r--r-- | src/app/manga/manga.module.css | 145 | ||||
| -rw-r--r-- | src/app/manga/page.jsx | 62 | ||||
| -rw-r--r-- | src/app/manga/searchBar.jsx | 35 | ||||
| -rw-r--r-- | src/app/page.module.css | 2 | ||||
| -rw-r--r-- | src/app/video/[animeId]/page.jsx | 3 |
15 files changed, 810 insertions, 6 deletions
diff --git a/src/app/globals.css b/src/app/globals.css index 42eb9ad..a76fc50 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -34,6 +34,7 @@ body::-webkit-scrollbar { .rightNav { display: flex; cursor: pointer; + overflow-x: auto; } .rightNav a { diff --git a/src/app/header/header.jsx b/src/app/header/header.jsx index bc4a019..22798b4 100644 --- a/src/app/header/header.jsx +++ b/src/app/header/header.jsx @@ -8,17 +8,20 @@ export default function Header() { href="/" style={{ color: "black", textDecoration: "none" }} > - <p style={{ fontSize: "30px", color: "var(--pastel-red)" }}> + <p style={{ fontSize: "26px", color: "var(--pastel-red)" }}> Dramalama </p> </Link> <div className="rightNav"> - <Link href="/kdrama"> - <p>Kdrama</p> - </Link> <Link href="/anime"> <p>Anime</p> </Link> + <Link href="/manga"> + <p>Manga</p> + </Link> + <Link href="/kdrama"> + <p>Kdrama</p> + </Link> </div> </div> <hr style={{ marginTop: "-3px" }} /> diff --git a/src/app/manga/Manga.jsx b/src/app/manga/Manga.jsx new file mode 100644 index 0000000..c4a0410 --- /dev/null +++ b/src/app/manga/Manga.jsx @@ -0,0 +1,63 @@ +import styles from "./manga.module.css"; +import Image from "next/image"; + +export default async function Manga() { + return ( + <div className={styles.Main}> + <div className={styles.Hero}> + <div className={styles.WelcomeContainer}> + <div className={styles.WelcomeText}> + Manga madness is here + </div> + <button>Let's Start</button> + </div> + <div className={styles.ImageContainer}> + <div className={styles.HorizontalImageContainer}> + <Image + src="/image.png" + width={480} + height={260} + alt="Haikyu" + /> + </div> + <div className={styles.VerticalImageContainer}> + <Image + src="/haikyu1.jpg" + width={240} + height={360} + alt="Haikyu" + /> + <Image + src="/solo_levelling.png" + width={240} + height={360} + alt="Haikyu" + /> + </div> + </div> + </div> + + <div className={styles.SelfPromoContainer}> + <div className={styles.Welcome1}> + <h2 style={{ textAlign: "center" }}> + Welcome to Dramalama Manga + </h2> + <p> + Dive into a world where action jumps off the page and + pictures paint a thousand words. Manga Universe is a + site that will immerse you in stunning illustrations and + compel you to lose yourself in thrilling narratives. + </p> + </div> + <div className={styles.ChooseusContainer}> + <h2 style={{ textAlign: "center" }}>Why choose us?</h2> + <p> + Our platform is built by manga enthusiasts, for manga + enthusiasts. That means high quality scans, an extensive + series catalogue, and regular series updates. + </p> + </div> + </div> + </div> + ); +} diff --git a/src/app/manga/[title]/[id]/[read]/page.jsx b/src/app/manga/[title]/[id]/[read]/page.jsx new file mode 100644 index 0000000..a90d170 --- /dev/null +++ b/src/app/manga/[title]/[id]/[read]/page.jsx @@ -0,0 +1,51 @@ +import styles from "./read.module.css"; +import Image from "next/image"; + +export default async function Read({ params }) { + const chapterId = params.read; + const data = await getPages(chapterId); + if (data.length === 0) { + return ( + <div className={styles.NotFound}> + <p> + This chapter has no content. Please check the next chapter. + </p> + </div> + ); + } + + let images = []; + for (var i = 0; i < data.length; i++) { + var imgUrl = data[i].img; + images.push(imgUrl); + } + + return ( + <div className={styles.Main}> + <div className={styles.ImageContainer}> + {images && + images.map((item, index) => ( + <div className={styles.Image}> + <Image + src={`https://image-proxy-manga.vercel.app/image-proxy?url=${item}`} + key={index} + alt="Pages" + width={800} + height={1000} + priority + /> + <p>{index + 1}</p> + </div> + ))} + </div> + </div> + ); +} + +async function getPages(id) { + const res = await fetch( + `https://consumet-api-di2e.onrender.com/meta/anilist-manga/read?chapterId=${id}&provider=mangadex` + ); + const data = await res.json(); + return data; +} diff --git a/src/app/manga/[title]/[id]/[read]/read.module.css b/src/app/manga/[title]/[id]/[read]/read.module.css new file mode 100644 index 0000000..3a8c99f --- /dev/null +++ b/src/app/manga/[title]/[id]/[read]/read.module.css @@ -0,0 +1,43 @@ +.ImageContainer img { + width: auto; + height: auto; + border-radius: 5px; + margin-top: 10px; + +} + +.Image { + display: flex; + flex-direction: column; + align-items: center; + background-color: #1b1b1b; + border-radius: 10px; + width: auto; + margin-top: 10px; +} + +.ImageContainer p { + text-align: center; + color: white; + font-family: "Kanit"; + font-size: 16px; + margin: 5px; +} + +.NotFound { + text-align: center; + color: white; + font-family: "Atkinson Hyperlegible"; + font-size: 20px; +} + +@media screen and (max-width: 768px) { + .ImageContainer img { + width: 90%; + align-items: center; + } + + .Image { + width: 100%; + } +}
\ No newline at end of file diff --git a/src/app/manga/[title]/[id]/buttons.jsx b/src/app/manga/[title]/[id]/buttons.jsx new file mode 100644 index 0000000..07fe3c3 --- /dev/null +++ b/src/app/manga/[title]/[id]/buttons.jsx @@ -0,0 +1,28 @@ +import styles from "./info.module.css"; +import Link from "next/link"; + +export default async function Buttons({ content: data }) { + return ( + <div className={styles.ChapterContainer}> + {data.chapters && + data.chapters.map((item, index) => { + if (item.pages !== 0) { + return ( + <Link + href={{ + pathname: `/manga/info/read/${item.id}`, + query: { + name: item.title, + }, + }} + > + <button key={index}> + {item.volumeNumber} - {item.chapterNumber} + </button> + </Link> + ); + } + })} + </div> + ); +} diff --git a/src/app/manga/[title]/[id]/info.module.css b/src/app/manga/[title]/[id]/info.module.css new file mode 100644 index 0000000..99ea636 --- /dev/null +++ b/src/app/manga/[title]/[id]/info.module.css @@ -0,0 +1,135 @@ +.MangaInfoContainer { + max-width: 90%; + margin: 0px auto; +} + +.MangaHero { + display: flex; + flex-direction: column; +} + +.HeroImage { + width: 100%; + height: auto; + max-height: 550px; + border-radius: 10px; +} + +.TitleContainer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 10px; +} + +.TitleContainer p { + font-family: "Lato"; + font-size: 32px; +} + +.TitleContainer img { + border-radius: 10px; +} + +.MangaDescription { + color: white; + font-family: "Atkinson Hyperlegible"; + text-align: center; +} + +.MangaDescription span { + margin: 10px; + color: var(--soft-purple); +} + +.MangaRatings { + color: var(--light-green); +} + +.MangaGenre { + background-color: #5f5f5f5d; + padding: 2px; + border-radius: 5px; + cursor: pointer; +} + +.Character { + display: flex; + flex-direction: row; + overflow-x: auto; +} + +.Character::-webkit-scrollbar { + height: 5px; +} + +.Character::-webkit-scrollbar-thumb { + background-color: #949494; + border-radius: 5px; +} + +.Character::-webkit-scrollbar-track { + background-color: rgb(87, 87, 87); + border-radius: 5px; +} + +.CharacterEntry { + margin: 5px; +} + +.CharacterEntry p { + text-align: center; + color: white; +} + +.CharacterEntry img { + border-radius: 10px; +} + +/* Chapters Buttons */ +.ChapterContainer { + width: 95%; + margin: 20px auto; + text-align: center; + height: 400px; + overflow-y: auto; +} + +.ChapterContainer::-webkit-scrollbar { + width: 5px; +} + +.ChapterContainer::-webkit-scrollbar-thumb { + background-color: #949494; + border-radius: 5px; +} + +.ChapterContainer::-webkit-scrollbar-track { + background-color: rgb(66, 66, 66); + border-radius: 5px; +} + +.ChapterContainer button { + width: 100px; + padding: 10px; + margin: 5px; + border-radius: 5px; + font-size: 16px; + border: none; + outline: none; + font-family: "Lato"; + background-color: #41C9E2; + cursor: pointer; + transition: transform 0.2s linear; + +} + +.ChapterContainer button:hover { + background-color: var(--neon-green); +} + +.ChapterContainer button:focus { + opacity: 0.6; + transition: transform 0.2s linear; + transform: scale(0.9); +}
\ No newline at end of file diff --git a/src/app/manga/[title]/[id]/page.jsx b/src/app/manga/[title]/[id]/page.jsx new file mode 100644 index 0000000..b41f719 --- /dev/null +++ b/src/app/manga/[title]/[id]/page.jsx @@ -0,0 +1,102 @@ +import styles from "./info.module.css"; +import Image from "next/image"; +import Buttons from "./buttons"; + +export default async function MangaInfo({ params }) { + const id = params.id; + const data = await getMangaInfo(id); + + return ( + <div className={styles.MangaInfoContainer}> + {data && ( + <div className={styles.MangaInfo}> + <div className={styles.MangaHero}> + <Image + src={data.cover} + width={1730} + height={400} + alt="Cover Poster" + className={styles.HeroImage} + priority + /> + <div className={styles.TitleContainer}> + <p style={{ color: data.color }}> + {data.title["romaji"]} + </p> + <Image + src={data.image} + width={200} + height={310} + alt="Manga Poster" + priority + /> + </div> + </div> + + <div className={styles.MangaDescription}> + <p>{data.description.split("<br")[0]}</p> + <span className={styles.MangaReleaseYear}> + Released in: {data.releaseDate} + </span> + <span> + Started on: {data.startDate["day"]}- + {data.startDate["month"]}-{data.startDate["year"]} + </span> + <span> + Ended on: {data.endDate["day"]}- + {data.endDate["month"]}-{data.endDate["year"]} + </span> + <p className={styles.MangaRatings}> + Ratings: {data.rating / 10} + </p> + <p style={{ color: "#7ED7C1" }}> + Genres: + {data.genres && + data.genres.map((item, index) => ( + <span + key={index} + className={styles.MangaGenre} + style={{ color: data.color, margin: 5 }} + > + {item} + </span> + ))} + </p> + </div> + + <div className={styles.CharactersContainer}> + <div className={styles.Character}> + {data.characters && + data.characters.map((item, index) => ( + <div + key={index} + className={styles.CharacterEntry} + > + <Image + src={item.image} + width={140} + height={200} + alt="Character Poster" + /> + <p> + {item.name.full} ({item.role}) + </p> + </div> + ))} + </div> + </div> + + <Buttons content={data} /> + </div> + )} + </div> + ); +} + +async function getMangaInfo(id) { + const res = await fetch( + `https://consumet-api-di2e.onrender.com/meta/anilist-manga/info/${id}?provider=mangadex` + ); + const data = await res.json(); + return data; +} diff --git a/src/app/manga/[title]/page.jsx b/src/app/manga/[title]/page.jsx new file mode 100644 index 0000000..92c2897 --- /dev/null +++ b/src/app/manga/[title]/page.jsx @@ -0,0 +1,74 @@ +import styles from "./title.module.css"; +import Image from "next/image"; +import Link from "next/link"; + +export default async function MangaInfo({ params }) { + const title = params.title; + const data = await GetSearchedAnime(title); + + return ( + <div className={styles.Main}> + <div className={styles.MangaContainer}> + {title && ( + <div className={styles.SearchedFor}> + <p>Searched for: {decodeURIComponent(title)}</p> + </div> + )} + {data && + data.results.map(async (item, index) => { + let desc = item.description || ""; // Ensure desc is not null + if (desc === "") { + desc = "Not found"; // If desc is empty, set it to "Not found" + } + return ( + <Link + href={`./info/${item.id}`} + style={{ textDecoration: "none" }} + key={index} + > + <div className={styles.MangaEntries}> + <Image + src={item.image} + width={140} + height={240} + alt="Manga Poster" + style={{ borderRadius: 10 }} + /> + <div className={styles.MangaInfo}> + <p className={styles.MangaTitle}> + {item.title["romaji"]},{" "} + {item.title["english"]},{" "} + {item.title["native"]} + </p> + <p className={styles.MangaDescription}> + {desc.includes && + desc.includes("<br") + ? desc.split("<b")[0] + : desc} + </p> + <p className={styles.MangaStatus}> + {item.status} + </p> + <p className={styles.MangaChapters}> + Chapters: {item.totalChapters} + </p> + <p className={styles.MangaVolume}> + Volumes: {item.volumes} + </p> + </div> + </div> + </Link> + ); + })} + </div> + </div> + ); +} + +async function GetSearchedAnime(title) { + const res = await fetch( + "https://consumet-api-di2e.onrender.com/meta/anilist-manga/" + title + ); + const data = await res.json(); + return data; +} diff --git a/src/app/manga/[title]/title.module.css b/src/app/manga/[title]/title.module.css new file mode 100644 index 0000000..eef08ba --- /dev/null +++ b/src/app/manga/[title]/title.module.css @@ -0,0 +1,61 @@ +.Main { + max-width: 90%; + margin: 0px auto; +} + +.MangaContainer { + display: flex; + flex-direction: column; +} + +.SearchedFor { + color: white; + text-align: center; + font-family: "Kanit"; + font-size: 26px; +} + +.MangaEntries { + display: flex; + flex-direction: row; + margin: 10px; + padding: 10px; + border-style: dotted; + border-color: #363636 #474747; + border-radius: 10px; + border-width: 4px; + align-items: center; + cursor: pointer; + transition: transform 0.2s linear; +} + +.MangaEntries:hover { + transition: transform 0.2s linear; + transform: scale(1.01); + border-color: #535353 #686868; + +} + +.MangaInfo { + color: white; + margin-left: 20px; +} + +.MangaTitle { + font-family: "Lato"; + margin: 0px; + font-size: 22px; + color: var(--neon-green); +} + +.MangaStatus { + color: var(--soft-purple); +} + +.MangaVolume { + color: #FFACAC; +} + +.MangaChapters { + color: #FFEBB4 +}
\ No newline at end of file diff --git a/src/app/manga/manga.module.css b/src/app/manga/manga.module.css new file mode 100644 index 0000000..7e4a5ba --- /dev/null +++ b/src/app/manga/manga.module.css @@ -0,0 +1,145 @@ +@import url('https://fonts.googleapis.com/css2?family=Glass+Antiqua&family=Inter&display=swap'); + +.Main { + max-width: 90%; + margin: 10px auto; +} + +.Hero { + display: flex; + align-items: center; + justify-content: space-between; +} + +.ImageContainer { + display: flex; + flex-direction: column; +} + +.ImageContainer img { + margin: 0px 4px 4px 4px; + border-radius: 8px; + animation: zoomer 1s alternate-reverse infinite ease; +} + +@keyframes zoomer { + to { + transform: scale(1.01); + } +} + +.WelcomeContainer button { + font-family: "Inter"; + font-size: 16px; + margin-top: 8px; + padding: 6px; + border-radius: 5px; + border: none; + outline: none; + background-color: var(--neon-green); + cursor: pointer; +} + +.WelcomeText { + font-family: "Kanit"; + color: white; + font-size: 50px; + text-shadow: #FC0 2px 2px 50px; + margin-right: 10px; +} + + +.SelfPromoContainer { + display: flex; + justify-content: space-around; + font-family: "Lato"; + color: white; + /* margin-top: 20px; */ +} + +.SelfPromoContainer p { + font-family: "Quicksand"; + color: white; + text-align: center; +} + + +@media screen and (max-width: 1024px) { + + .HorizontalImageContainer img { + width: auto; + height: auto; + } + + .VerticalImageContainer img { + width: 265px; + } +} + +@media screen and (max-width: 768px) { + + .HorizontalImageContainer img { + width: 95%; + height: auto; + } + + .VerticalImageContainer img { + width: 46.5%; + height: 300px; + } +} + +.SearchBar { + display: flex; + align-items: center; + border-radius: 4px; + background: #1f1f1f; + margin-top: 5px; + width: 55%; +} + +.SearchBar input { + background: none; + outline: none; + border: none; + margin-left: 5px; + padding: 4px; + width: 100%; + color: white; + font-family: "Kanit"; + font-size: 16px; +} + +@media screen and (max-width: 425px) { + + .Hero { + flex-direction: column; + } + + .WelcomeContainer { + display: flex; + align-items: center; + flex-direction: column; + margin-bottom: 10px; + } + + .WelcomeText { + font-size: 32px; + } + + .HorizontalImageContainer img { + margin-top: 20px; + width: 100%; + height: auto; + } + + .VerticalImageContainer img { + display: none; + } + + .SearchBar { + width: 80%; + } +} + +/* Search Bar from searchBar.jsx */
\ No newline at end of file diff --git a/src/app/manga/page.jsx b/src/app/manga/page.jsx new file mode 100644 index 0000000..7f1f9cb --- /dev/null +++ b/src/app/manga/page.jsx @@ -0,0 +1,62 @@ +import styles from "./manga.module.css"; +import Image from "next/image"; +import SearchBar from "./searchBar"; + +export default async function Manga() { + return ( + <div className={styles.Main}> + <div className={styles.Hero}> + <div className={styles.WelcomeContainer}> + <div className={styles.WelcomeText}> + Manga madness is here + </div> + <SearchBar /> + </div> + <div className={styles.ImageContainer}> + <div className={styles.HorizontalImageContainer}> + <Image + src="/image.png" + width={480} + height={260} + alt="Haikyu" + /> + </div> + <div className={styles.VerticalImageContainer}> + <Image + src="/haikyu1.jpg" + width={240} + height={360} + alt="Haikyu" + /> + <Image + src="/solo_levelling.png" + width={240} + height={360} + alt="Haikyu" + /> + </div> + </div> + </div> + + <div className={styles.SelfPromoContainer}> + <div className={styles.Welcome1}> + <p + style={{ + textAlign: "center", + fontSize: 32, + color: "var(--soft-purple)", + }} + > + Welcome to Dramalama Manga + </p> + <p> + Dive into a world where action jumps off the page and + pictures paint a thousand words. Dramalama Manga is a + site that will immerse you in stunning illustrations and + compel you to lose yourself in thrilling narratives. + </p> + </div> + </div> + </div> + ); +} diff --git a/src/app/manga/searchBar.jsx b/src/app/manga/searchBar.jsx new file mode 100644 index 0000000..fa0962a --- /dev/null +++ b/src/app/manga/searchBar.jsx @@ -0,0 +1,35 @@ +"use client"; + +import { FaSearch } from "react-icons/fa"; +import styles from "./manga.module.css"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; + +export default function SearchBar() { + const router = useRouter(); + + const [title, setMangaTitle] = useState(""); + + return ( + <div className={styles.SearchBar}> + <FaSearch color="white" style={{ marginLeft: 5 }} /> + <input + type="text" + name="manga" + placeholder="Enter manga title" + autoComplete="off" + onChange={(e) => setMangaTitle(e.target.value)} + onKeyDown={(event) => { + if ( + (event.key === "Enter" || + event.code === 13 || + event.code === "Enter") && + title !== "" + ) { + router.push(`/manga/${title}`); + } + }} + ></input> + </div> + ); +} diff --git a/src/app/page.module.css b/src/app/page.module.css index c8bd07e..9e8ddad 100644 --- a/src/app/page.module.css +++ b/src/app/page.module.css @@ -3,7 +3,7 @@ flex-direction: column; justify-content: center; align-items: center; - height: 90dvh; + height: 85dvh; max-width: 500px; margin: 0px auto; text-align: center; diff --git a/src/app/video/[animeId]/page.jsx b/src/app/video/[animeId]/page.jsx index cfa8cd5..c0339f7 100644 --- a/src/app/video/[animeId]/page.jsx +++ b/src/app/video/[animeId]/page.jsx @@ -48,7 +48,8 @@ export default async function Video({ params }) { async function getVideoLink(id) { const res = await fetch( - "https://consumet-api-di2e.onrender.com/anime/gogoanime/watch/" + id + "https://consumet-api-di2e.onrender.com/anime/gogoanime/watch/" + id, + { next: { revalidate: 3600 } } // Video links are revalidated after an hour ); const data = res.json(); return data; |