aboutsummaryrefslogtreecommitdiff
path: root/src/app/anime/components
diff options
context:
space:
mode:
authorreal-zephex <[email protected]>2024-05-24 22:51:36 +0530
committerreal-zephex <[email protected]>2024-05-24 22:51:36 +0530
commit180c9577f8337991ca71470816333fe8430cd3ca (patch)
tree82caa5a920443bcf0db3746c7ecacd968d4fc148 /src/app/anime/components
parentstyle: minor improvements to the anime cards (diff)
downloaddramalama-180c9577f8337991ca71470816333fe8430cd3ca.tar.xz
dramalama-180c9577f8337991ca71470816333fe8430cd3ca.zip
✨ feat(ui): 🎨 migrate from vanilla css to tailwind css, adopted next ui and restructured
Diffstat (limited to 'src/app/anime/components')
-rw-r--r--src/app/anime/components/cacher.js4
-rw-r--r--src/app/anime/components/episode_buttons.jsx2
-rw-r--r--src/app/anime/components/infoTabs.jsx46
-rw-r--r--src/app/anime/components/popularAnimes.jsx53
-rw-r--r--src/app/anime/components/recentEpisodes.jsx54
-rw-r--r--src/app/anime/components/saveToLocalStorage.js29
-rw-r--r--src/app/anime/components/search.jsx55
-rw-r--r--src/app/anime/components/search_results.jsx51
-rw-r--r--src/app/anime/components/topAiring.jsx56
-rw-r--r--src/app/anime/components/vidButtonContainer.jsx133
10 files changed, 270 insertions, 213 deletions
diff --git a/src/app/anime/components/cacher.js b/src/app/anime/components/cacher.js
index d3008fa..164dafd 100644
--- a/src/app/anime/components/cacher.js
+++ b/src/app/anime/components/cacher.js
@@ -1,11 +1,11 @@
"use server";
-import { info_url, watch_url } from "../../../../utils/anime_urls";
+import { anime_info } from "../data-fetch/request";
export async function preFetchAnimeInfo(data) {
try {
const fetchPromises = data.results.map(async (element) => {
- await fetch(info_url(element.id), { next: { revalidate: 21600 } });
+ await anime_info(element.id);
});
await Promise.all(fetchPromises);
diff --git a/src/app/anime/components/episode_buttons.jsx b/src/app/anime/components/episode_buttons.jsx
index 013dee1..71f338f 100644
--- a/src/app/anime/components/episode_buttons.jsx
+++ b/src/app/anime/components/episode_buttons.jsx
@@ -202,4 +202,4 @@ function store_to_local(name, image, episode, id) {
}
}
-export default EpisodesButtons; \ No newline at end of file
+export default EpisodesButtons;
diff --git a/src/app/anime/components/infoTabs.jsx b/src/app/anime/components/infoTabs.jsx
new file mode 100644
index 0000000..68a1da1
--- /dev/null
+++ b/src/app/anime/components/infoTabs.jsx
@@ -0,0 +1,46 @@
+"use client";
+
+import { Tabs, Tab, Card, CardBody } from "@nextui-org/react";
+
+import { lexend, atkinson } from "../../../../config/fonts";
+
+export default function DescriptionTabs({ data: data }) {
+ return (
+ <div className="flex w-full flex-col">
+ <Tabs aria-label="Options" className={lexend.className}>
+ <Tab key="description" title="Description">
+ <Card>
+ <CardBody className={atkinson.className}>
+ {data.description || "No description found"}
+ </CardBody>
+ </Card>
+ </Tab>
+ <Tab key="episodes" title="Details">
+ <Card>
+ <CardBody className={atkinson.className}>
+ <h4>
+ <strong>Episodes</strong>:{" "}
+ <span>{data.totalEpisodes}</span>
+ </h4>
+ <h4>
+ <strong>Type</strong>: <span>{data.type}</span>
+ </h4>
+ <h4>
+ <strong>SUB/DUB</strong>:{" "}
+ <span>{data.subOrDub.toUpperCase()}</span>
+ </h4>
+ <h4>
+ <strong>Status</strong>:{" "}
+ <span>{data.status}</span>
+ </h4>
+ <h4>
+ <strong>Release Year</strong>:{" "}
+ <span>{data.releaseDate}</span>
+ </h4>
+ </CardBody>
+ </Card>
+ </Tab>
+ </Tabs>
+ </div>
+ );
+}
diff --git a/src/app/anime/components/popularAnimes.jsx b/src/app/anime/components/popularAnimes.jsx
deleted file mode 100644
index e62f70f..0000000
--- a/src/app/anime/components/popularAnimes.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { Lexend_Deca } from "next/font/google";
-
-import styles from "../styles/pop_recent_top.module.css";
-import { popular } from "../data-fetch/request";
-import { preFetchAnimeInfo } from "./cacher";
-
-const lexend = Lexend_Deca({ subsets: ["latin"], weight: "400" });
-
-const PopularAnimes = async () => {
- const data = await popular();
-
- preFetchAnimeInfo(data);
-
- return (
- <main className={styles.Main}>
- <section>
- <h2 className={styles.AnimeHeaderText}>Popular Animes</h2>
- <div className={styles.AnimeContainer}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- shallow
- style={{
- color: "white",
- textDecoration: "none",
- }}
- className={lexend.className}
- title={item.title}
- >
- <section className={styles.AnimeEntry}>
- <Image
- src={item.image}
- width={180}
- height={300}
- alt="Anime Poster Image"
- />
- <p className={styles.AnimeTitle}>
- {item.title}
- </p>
- </section>
- </Link>
- ))}
- </div>
- </section>
- </main>
- );
-};
-
-export default PopularAnimes;
diff --git a/src/app/anime/components/recentEpisodes.jsx b/src/app/anime/components/recentEpisodes.jsx
deleted file mode 100644
index 9e7899f..0000000
--- a/src/app/anime/components/recentEpisodes.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { Lexend_Deca } from "next/font/google";
-
-import styles from "../styles/pop_recent_top.module.css";
-import { recent } from "../data-fetch/request";
-import { preFetchAnimeInfo } from "./cacher";
-
-const lexend = Lexend_Deca({ subsets: ["latin"], weight: "400" });
-
-const RecentAnimes = async () => {
- const data = await recent();
-
- preFetchAnimeInfo(data);
-
- return (
- <main className={styles.Main}>
- <section>
- <h2 className={styles.AnimeHeaderText}>Recent Releases</h2>
- <div className={styles.AnimeContainer}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- shallow
- style={{
- color: "white",
- textDecoration: "none",
- }}
- className={lexend.className}
- title={item.title}
- >
- <section className={styles.AnimeEntry}>
- <Image
- src={item.image}
- width={180}
- height={300}
- alt="Anime Poster Image"
- />
- <p className={styles.AnimeTitle}>
- {item.title}
- </p>
- <p className={styles.AnimeReleasedEpisode}>{item.episodeNumber}</p>
- </section>
- </Link>
- ))}
- </div>
- </section>
- </main>
- );
-};
-
-export default RecentAnimes;
diff --git a/src/app/anime/components/saveToLocalStorage.js b/src/app/anime/components/saveToLocalStorage.js
new file mode 100644
index 0000000..313b157
--- /dev/null
+++ b/src/app/anime/components/saveToLocalStorage.js
@@ -0,0 +1,29 @@
+"use client";
+
+import { storeLocal } from "./storeHistory";
+
+export default function store_to_local(name, image, episode, id) {
+ const currentDate = new Date();
+
+ try {
+ let newData = {
+ name: name,
+ image: image,
+ episode: episode,
+ id: id,
+ type: "anime",
+ date: `${currentDate.getDate()}-${String(
+ currentDate.getMonth() + 1
+ ).padStart(2, "0")}`,
+ time: `${currentDate.getHours()}:${String(
+ currentDate.getMinutes()
+ ).padStart(2, "0")}`,
+ };
+ storeLocal(newData);
+ } catch (error) {
+ console.error(
+ "Some error occurred during the process of saving your watch history to local storage. Please try again or contact us on GitHub if this issue persists.",
+ error.message
+ );
+ }
+}
diff --git a/src/app/anime/components/search.jsx b/src/app/anime/components/search.jsx
index 0a780f8..0fe883b 100644
--- a/src/app/anime/components/search.jsx
+++ b/src/app/anime/components/search.jsx
@@ -2,37 +2,37 @@
import { FaSearch } from "react-icons/fa";
import { useState } from "react";
-import Link from "next/link";
+import { Input, Link, Button, Progress } from "@nextui-org/react";
-import styles from "../styles/search.module.css";
import SearchResults from "./search_results";
-const SearcBar = () => {
+const SearchBar = () => {
const [title, setTitle] = useState("");
const [searchResults, setSearchResults] = useState(null);
const [loading, setLoading] = useState(false);
const handleSearchInput = async (title) => {
setSearchResults(null);
- setLoading(true);
+ setLoading(
+ <Progress
+ size="sm"
+ isIndeterminate
+ aria-label="Loading..."
+ className="w-full mt-2 lg:w-1/2"
+ />
+ );
setSearchResults(await SearchResults(title));
setLoading(false);
};
return (
<main>
- <section className={styles.SearchBarContainer}>
- <div className={styles.SearchInputContainer}>
- <FaSearch color="white" size={22} />
- <input
- placeholder="Enter anime title"
- name="Anime Title"
+ <section>
+ <div className="flex w-full md:flex-nowrap gap-2 lg:w-1/2">
+ <Input
type="text"
- onChange={(event) => {
- if (event.target.value.trim() != "") {
- setTitle(event.target.value);
- }
- }}
+ label="Search for anime"
+ placeholder="Enter anime title here"
autoComplete="off"
onKeyDown={async (event) => {
if (
@@ -43,22 +43,23 @@ const SearcBar = () => {
await handleSearchInput(title);
}
}}
- ></input>
- <Link shallow href={"/anime/continueWatching"}>
- <button className={styles.animeHistoryButton}>
- History
- </button>
+ onChange={(event) => {
+ if (event.target.value.trim() != "") {
+ setTitle(event.target.value);
+ }
+ }}
+ startContent={<FaSearch />}
+ />
+
+ <Link href={"/anime/continueWatching"}>
+ <Button color="primary">History</Button>
</Link>
</div>
+ {loading}
</section>
- {loading && (
- <p className={styles.SearchLoading}>
- Please wait while we crunch up all the data.....
- </p>
- )}
- {searchResults}
+ <div className="mt-2">{searchResults}</div>
</main>
);
};
-export default SearcBar;
+export default SearchBar;
diff --git a/src/app/anime/components/search_results.jsx b/src/app/anime/components/search_results.jsx
index 051124d..3097a96 100644
--- a/src/app/anime/components/search_results.jsx
+++ b/src/app/anime/components/search_results.jsx
@@ -1,12 +1,9 @@
-import { Lexend_Deca } from "next/font/google";
-import Link from "next/link";
-import Image from "next/image";
-
-import styles from "../styles/search.module.css";
import { search_results } from "../data-fetch/request";
import { preFetchAnimeInfo } from "./cacher";
+import styles from "../../page.module.css";
-const lexend = Lexend_Deca({ subsets: ["latin"], weight: "400" });
+import { Card, CardHeader, CardBody, Image, Link } from "@nextui-org/react";
+import NextImage from "next/image";
const SearchResults = async (title) => {
const data = await search_results(title);
@@ -14,25 +11,39 @@ const SearchResults = async (title) => {
preFetchAnimeInfo(data);
return (
- <section className={styles.SearchResultsContainer}>
+ <section
+ className={`flex items-center overflow-auto pb-2 ${styles.ScrollBarAdjuster}`}
+ >
{data &&
data.results.map((item, index) => (
<Link
- shallow
- href={`/anime/${item.id}`}
key={index}
- className={lexend.className}
- style={{ color: "white", textDecoration: "none" }}
+ href={`/anime/${item.id}`}
+ aria-label="anime redirection links"
+ className="flex flex-col items-center mx-1 "
>
- <div className={styles.AnimeEntry}>
- <Image
- src={item.image}
- width={180}
- height={300}
- alt="Anime Poster"
- />
- <p>{item.title}</p>
- </div>
+ <Card className="overflow-hidden" isPressable>
+ <CardBody>
+ <Image
+ as={NextImage}
+ isBlurred
+ alt="Anime Poster"
+ src={item.image}
+ width={190}
+ height={120}
+ shadow="lg"
+ className="h-64"
+ priority
+ />
+ </CardBody>
+ <CardHeader>
+ <h4
+ className={`antialiased text-small text-center uppercase w-44 overflow-hidden whitespace-nowrap text-ellipsis `}
+ >
+ {item.title}
+ </h4>
+ </CardHeader>
+ </Card>
</Link>
))}
</section>
diff --git a/src/app/anime/components/topAiring.jsx b/src/app/anime/components/topAiring.jsx
deleted file mode 100644
index 22e8c3b..0000000
--- a/src/app/anime/components/topAiring.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { Lexend_Deca } from "next/font/google";
-
-import styles from "../styles/pop_recent_top.module.css";
-import { top_airing } from "../data-fetch/request";
-import { preFetchAnimeInfo } from "./cacher";
-
-const lexend = Lexend_Deca({ subsets: ["latin"], weight: "400" });
-
-const TopAiringAnimes = async () => {
- const data = await top_airing();
-
- preFetchAnimeInfo(data);
-
- return (
- <main className={styles.Main}>
- <section>
- <h2 className={styles.AnimeHeaderText}>Top Airing Animes</h2>
- <div className={styles.AnimeContainer}>
- {data &&
- data.results.map((item, index) => (
- <Link
- key={index}
- href={`/anime/${item.id}`}
- shallow
- style={{
- color: "white",
- textDecoration: "none",
- }}
- className={lexend.className}
- title={item.title}
- >
- <section className={styles.AnimeEntry}>
- <Image
- src={item.image}
- width={180}
- height={300}
- alt="Anime Poster Image"
- />
- <p className={styles.AnimeTitle}>
- {item.title}
- </p>
- <p className={styles.AnimeReleasedEpisode}>
- {item.episodeNumber}
- </p>
- </section>
- </Link>
- ))}
- </div>
- </section>
- </main>
- );
-};
-
-export default TopAiringAnimes;
diff --git a/src/app/anime/components/vidButtonContainer.jsx b/src/app/anime/components/vidButtonContainer.jsx
new file mode 100644
index 0000000..6b872d8
--- /dev/null
+++ b/src/app/anime/components/vidButtonContainer.jsx
@@ -0,0 +1,133 @@
+"use client";
+
+import { MediaPlayer, MediaProvider } from "@vidstack/react";
+import "@vidstack/react/player/styles/default/theme.css";
+import "@vidstack/react/player/styles/default/layouts/video.css";
+import {
+ defaultLayoutIcons,
+ DefaultVideoLayout,
+} from "@vidstack/react/player/layouts/default";
+import { Select, SelectItem, Button, Skeleton } from "@nextui-org/react";
+import { useState, useEffect } from "react";
+
+import { lexend } from "../../../../config/fonts";
+import { video_url } from "../data-fetch/request";
+import store_to_local from "./saveToLocalStorage";
+
+const EpisodesContainer = ({ data: data }) => {
+ const [videoLink, setVideoLink] = useState("");
+ const [buttonGroups, setButtonGroups] = useState(<></>);
+ const [videoLoading, setVideoLoading] = useState(<></>);
+
+ useEffect(() => {
+ setButtonGroups(createButtonGroups(0, 50));
+ }, []);
+
+ const groups = createGroups(data.episodes, 50);
+
+ function createButtonGroups(start, end) {
+ setButtonGroups(<></>);
+ return (
+ <div className={`${lexend.className} text-center`}>
+ {data.episodes &&
+ data.episodes.slice(start, end).map((item, index) => (
+ <Button
+ radius="sm"
+ color="default"
+ key={index}
+ className="mr-2 mt-2"
+ size="sm"
+ onClick={async () => {
+ await changeVideoLink(item.id);
+ store_to_local(
+ data.title,
+ data.image,
+ item.number,
+ data.id
+ );
+ }}
+ >
+ {item.number}
+ </Button>
+ ))}
+ </div>
+ );
+ }
+
+ function handleSelectChange(item) {
+ // console.log(item[item.length - 1].number);
+ const start_index = item[0].number;
+ const end_index = item[item.length - 1].number;
+ setButtonGroups(createButtonGroups(start_index - 1, end_index));
+ }
+
+ async function changeVideoLink(id) {
+ setVideoLink("");
+ setVideoLoading(
+ <div className="w-full flex items-center gap-3">
+ <div className="w-full flex flex-col gap-2">
+ <Skeleton className="h-44 rounded-lg lg:h-96" />
+ </div>
+ </div>
+ );
+ const videoURL = await video_url(id);
+ setVideoLoading(<></>);
+ setVideoLink(videoURL.sources[videoURL.sources.length - 2].url);
+ }
+
+ return (
+ <main>
+ {videoLoading}
+ {videoLink && (
+ <div>
+ <MediaPlayer
+ title={data.title}
+ src={videoLink}
+ aspectRatio="16/9"
+ load="eager"
+ playsInline
+ volume={0.8}
+ autoPlay
+ >
+ <MediaProvider />
+ <DefaultVideoLayout icons={defaultLayoutIcons} />
+ </MediaPlayer>
+ </div>
+ )}
+ {data.episodes && (
+ <div className="flex w-full flex-wrap md:flex-nowrap gap-4 my-2">
+ <Select
+ label="Select Episode Group"
+ className={`${lexend.className} max-w-xs`}
+ >
+ {groups &&
+ groups.map((item, index) => (
+ <SelectItem
+ key={index}
+ textValue={`${item[0].number} - ${
+ item[item.length - 1].number
+ }`}
+ onClick={() => handleSelectChange(item)}
+ className={lexend.className}
+ >
+ {item[0].number} -{" "}
+ {item[item.length - 1].number}
+ </SelectItem>
+ ))}
+ </Select>
+ </div>
+ )}
+ {buttonGroups}
+ </main>
+ );
+};
+
+function createGroups(array, size) {
+ const groups = [];
+ for (let i = 0; i < array.length; i += size) {
+ groups.push(array.slice(i, i + size));
+ }
+ return groups;
+}
+
+export default EpisodesContainer;