aboutsummaryrefslogtreecommitdiff
path: root/components/home
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-09-13 00:45:53 +0700
committerGitHub <[email protected]>2023-09-13 00:45:53 +0700
commit7327a69b55a20b99b14ee0803d6cf5f8b88c45ef (patch)
treecbcca777593a8cc4b0282e7d85a6fc51ba517e25 /components/home
parentUpdate issue templates (diff)
downloadmoopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.tar.xz
moopa-7327a69b55a20b99b14ee0803d6cf5f8b88c45ef.zip
Update v4 - Merge pre-push to main (#71)
* Create build-test.yml * initial v4 commit * update: github workflow * update: push on branch * Update .github/ISSUE_TEMPLATE/bug_report.md * configuring next.config.js file
Diffstat (limited to 'components/home')
-rw-r--r--components/home/content.js254
-rw-r--r--components/home/content/historyOptions.js56
-rw-r--r--components/home/genres.js4
-rw-r--r--components/home/mobileNav.js202
-rw-r--r--components/home/recommendation.js91
-rw-r--r--components/home/schedule.js28
-rw-r--r--components/home/staticNav.js160
7 files changed, 435 insertions, 360 deletions
diff --git a/components/home/content.js b/components/home/content.js
index 70f0e3f..e18e5d8 100644
--- a/components/home/content.js
+++ b/components/home/content.js
@@ -1,5 +1,6 @@
import Link from "next/link";
-import React, { useState, useRef, useEffect } from "react";
+import React, { useState, useRef, useEffect, Fragment } from "react";
+import { useDraggable } from "react-use-draggable-scroll";
import Image from "next/image";
import { MdChevronRight } from "react-icons/md";
import {
@@ -14,6 +15,7 @@ import { ChevronLeftIcon } from "@heroicons/react/20/solid";
import { ExclamationCircleIcon, PlayIcon } from "@heroicons/react/24/solid";
import { useRouter } from "next/router";
import { toast } from "react-toastify";
+import HistoryOptions from "./content/historyOptions";
export default function Content({
ids,
@@ -26,11 +28,10 @@ export default function Content({
}) {
const router = useRouter();
- const [startX, setStartX] = useState(null);
- const containerRef = useRef(null);
+ const ref = useRef();
+ const { events } = useDraggable(ref);
const [cookie, setCookie] = useState(null);
- const [isDragging, setIsDragging] = useState(false);
const [clicked, setClicked] = useState(false);
const [lang, setLang] = useState("en");
@@ -55,39 +56,20 @@ export default function Content({
}
}, []);
- const handleMouseDown = (e) => {
- setIsDragging(true);
- setStartX(e.pageX - containerRef.current.offsetLeft);
- };
-
- 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();
- }
- };
-
const [scrollLeft, setScrollLeft] = useState(false);
const [scrollRight, setScrollRight] = useState(true);
const slideLeft = () => {
+ ref.current.classList.add("scroll-smooth");
var slider = document.getElementById(ids);
slider.scrollLeft = slider.scrollLeft - 500;
+ ref.current.classList.remove("scroll-smooth");
};
const slideRight = () => {
+ ref.current.classList.add("scroll-smooth");
var slider = document.getElementById(ids);
slider.scrollLeft = slider.scrollLeft + 500;
+ ref.current.classList.remove("scroll-smooth");
};
const handleScroll = (e) => {
@@ -128,6 +110,9 @@ export default function Content({
if (section === "Recently Watched") {
router.push(`/${lang}/anime/recently-watched`);
}
+ if (section === "New Episodes") {
+ router.push(`/${lang}/anime/recent`);
+ }
if (section === "Trending Now") {
router.push(`/${lang}/anime/trending`);
}
@@ -142,7 +127,7 @@ export default function Content({
}
};
- const removeItem = async (id) => {
+ const removeItem = async (id, aniId) => {
if (userName) {
// remove from database
const res = await fetch(`/api/user/update/episode`, {
@@ -152,24 +137,42 @@ export default function Content({
},
body: JSON.stringify({
name: userName,
- id: id,
+ id,
+ aniId,
}),
});
const data = await res.json();
- // remove from local storage
- const artplayerSettings =
- JSON.parse(localStorage.getItem("artplayer_settings")) || {};
- if (artplayerSettings[id]) {
- delete artplayerSettings[id];
- localStorage.setItem(
- "artplayer_settings",
- JSON.stringify(artplayerSettings)
- );
+ if (id) {
+ // remove from local storage
+ const artplayerSettings =
+ JSON.parse(localStorage.getItem("artplayer_settings")) || {};
+ if (artplayerSettings[id]) {
+ delete artplayerSettings[id];
+ localStorage.setItem(
+ "artplayer_settings",
+ JSON.stringify(artplayerSettings)
+ );
+ }
+ }
+ if (aniId) {
+ const currentData =
+ JSON.parse(localStorage.getItem("artplayer_settings")) || {};
+
+ const updatedData = {};
+
+ for (const key in currentData) {
+ const item = currentData[key];
+ if (item.aniId !== aniId) {
+ updatedData[key] = item;
+ }
+ }
+
+ localStorage.setItem("artplayer_settings", JSON.stringify(updatedData));
}
// update client
- setRemoved(id);
+ setRemoved(id || aniId);
if (data?.message === "Episode deleted") {
toast.success("Episode removed from history", {
@@ -182,17 +185,38 @@ export default function Content({
});
}
} else {
- const artplayerSettings =
- JSON.parse(localStorage.getItem("artplayer_settings")) || {};
- if (artplayerSettings[id]) {
- delete artplayerSettings[id];
- localStorage.setItem(
- "artplayer_settings",
- JSON.stringify(artplayerSettings)
- );
+ if (id) {
+ // remove from local storage
+ const artplayerSettings =
+ JSON.parse(localStorage.getItem("artplayer_settings")) || {};
+ if (artplayerSettings[id]) {
+ delete artplayerSettings[id];
+ localStorage.setItem(
+ "artplayer_settings",
+ JSON.stringify(artplayerSettings)
+ );
+ }
+ setRemoved(id);
+ }
+ if (aniId) {
+ const currentData =
+ JSON.parse(localStorage.getItem("artplayer_settings")) || {};
+
+ // Create a new object to store the updated data
+ const updatedData = {};
+
+ // Iterate through the current data and copy items with different aniId to the updated object
+ for (const key in currentData) {
+ const item = currentData[key];
+ if (item.aniId !== aniId) {
+ updatedData[key] = item;
+ }
+ }
+
+ // Update localStorage with the filtered data
+ localStorage.setItem("artplayer_settings", JSON.stringify(updatedData));
+ setRemoved(aniId);
}
-
- setRemoved(id);
}
};
@@ -218,13 +242,10 @@ export default function Content({
</div>
<div
id={ids}
- className="scroll flex h-full w-full select-none overflow-x-scroll overflow-y-hidden scrollbar-hide lg:gap-8 gap-4 lg:p-10 py-8 px-5 z-30 scroll-smooth"
+ className="flex h-full w-full select-none overflow-x-scroll overflow-y-hidden scrollbar-hide lg:gap-8 gap-4 lg:p-10 py-8 px-5 z-30"
onScroll={handleScroll}
- onMouseDown={handleMouseDown}
- onMouseUp={handleMouseUp}
- onMouseMove={handleMouseMove}
- onClick={handleClick}
- ref={containerRef}
+ {...events}
+ ref={ref}
>
{ids !== "recentlyWatched"
? slicedData?.map((anime) => {
@@ -241,14 +262,14 @@ export default function Content({
title={anime.title.romaji}
>
{ids === "onGoing" && (
- <div className="h-[190px] lg:h-[265px] w-[135px] lg:w-[185px] bg-gradient-to-b from-transparent to-black absolute z-40 rounded-md whitespace-normal font-karla group">
+ <div className="h-[190px] lg:h-[265px] w-[135px] lg:w-[185px] bg-gradient-to-b from-transparent to-black/90 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" />
+ <ExclamationCircleIcon className="w-7 h-7 absolute z-40 text-white -top-3 -right-3" />
)}
{checkProgress(progress) && (
<div
@@ -275,30 +296,52 @@ export default function Content({
</div>
</div>
)}
- <Image
- draggable={false}
- src={
- anime.image ||
- anime.coverImage?.extraLarge ||
- anime.coverImage?.large ||
- "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png"
- }
- alt={
- anime.title.romaji ||
- anime.title.english ||
- "coverImage"
- }
- width={500}
- height={300}
- placeholder="blur"
- blurDataURL={
- anime.image ||
- anime.coverImage?.extraLarge ||
- anime.coverImage?.large ||
- "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png"
- }
- className="z-20 h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md brightness-90"
- />
+ <div className="h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] rounded-md z-30">
+ {ids === "recentAdded" && (
+ <div className="absolute bg-gradient-to-b from-black/30 to-transparent from-5% to-30% top-0 z-30 w-full h-full rounded" />
+ )}
+ <Image
+ draggable={false}
+ src={
+ anime.image ||
+ anime.coverImage?.extraLarge ||
+ anime.coverImage?.large ||
+ "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png"
+ }
+ alt={
+ anime.title.romaji ||
+ anime.title.english ||
+ "coverImage"
+ }
+ width={500}
+ height={300}
+ placeholder="blur"
+ blurDataURL={
+ anime.image ||
+ anime.coverImage?.extraLarge ||
+ anime.coverImage?.large ||
+ "https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png"
+ }
+ className="z-20 h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] object-cover rounded-md brightness-90"
+ />
+ </div>
+ {ids === "recentAdded" && (
+ <Fragment>
+ <Image
+ src="/svg/episode-badge.svg"
+ alt="episode-bade"
+ width={200}
+ height={100}
+ className="w-24 lg:w-32 absolute top-1 -right-[12px] lg:-right-[17px] z-40"
+ />
+ <p className="absolute z-40 text-center w-[86px] lg:w-[110px] top-1 -right-2 lg:top-[5.5px] lg:-right-2 font-karla text-sm lg:text-base">
+ Episode{" "}
+ <span className="text-white">
+ {anime?.episodeNumber}
+ </span>
+ </p>
+ </Fragment>
+ )}
</Link>
{ids !== "onGoing" && (
<Link
@@ -307,7 +350,8 @@ export default function Content({
title={anime.title.romaji}
>
<h1 className="font-karla font-semibold xl:text-base text-[15px]">
- {anime.status === "RELEASING" ? (
+ {anime.status === "RELEASING" ||
+ ids === "recentAdded" ? (
<span className="dots bg-green-500" />
) : anime.status === "NOT_YET_RELEASED" ? (
<span className="dots bg-red-500" />
@@ -333,22 +377,50 @@ export default function Content({
key={i.watchId}
className="flex flex-col gap-2 shrink-0 cursor-pointer relative group/item"
>
- <div className="absolute z-40 top-1 right-1 group-hover/item:visible invisible hover:text-action">
- <div
- className="flex flex-col items-center group/delete"
+ <div className="absolute flex flex-col gap-1 z-40 top-1 right-1 transition-all duration-200 ease-out opacity-0 group-hover/item:opacity-100 scale-90 group-hover/item:scale-100 group-hover/item:visible invisible ">
+ {/* <button
+ type="button"
+ className="flex flex-col items-center group/delete relative"
onClick={() => removeItem(i.watchId)}
>
- <XMarkIcon className="w-6 h-6 shrink-0 bg-primary p-1 rounded-full" />
+ <XMarkIcon className="w-6 h-6 shrink-0 bg-primary p-1 rounded-full hover:text-action scale-100 hover:scale-105 transition-all duration-200 ease-out" />
<span className="absolute font-karla bg-secondary shadow-black shadow-2xl py-1 px-2 whitespace-nowrap text-white text-sm rounded-md right-7 -bottom-[2px] z-40 duration-300 transition-all ease-out group-hover/delete:visible group-hover/delete:scale-100 group-hover/delete:translate-x-0 group-hover/delete:opacity-100 opacity-0 translate-x-10 scale-50 invisible">
Remove from history
</span>
- </div>
+ </button> */}
+ <HistoryOptions
+ remove={removeItem}
+ watchId={i.watchId}
+ aniId={i.aniId}
+ />
+ {i?.nextId && (
+ <button
+ type="button"
+ className="flex flex-col items-center group/next relative"
+ onClick={() => {
+ router.push(
+ `/en/anime/watch/${i.aniId}/${
+ i.provider
+ }?id=${encodeURIComponent(i?.nextId)}&num=${
+ i?.nextNumber
+ }${i?.dub ? `&dub=${i?.dub}` : ""}`
+ );
+ }}
+ >
+ <ChevronRightIcon className="w-6 h-6 shrink-0 bg-primary p-1 rounded-full hover:text-action scale-100 hover:scale-105 transition-all duration-200 ease-out" />
+ <span className="absolute font-karla bg-secondary shadow-black shadow-2xl py-1 px-2 whitespace-nowrap text-white text-sm rounded-md right-7 -bottom-[2px] z-40 duration-300 transition-all ease-out group-hover/next:visible group-hover/next:scale-100 group-hover/next:translate-x-0 group-hover/next:opacity-100 opacity-0 translate-x-10 scale-50 invisible">
+ Play Next Episode
+ </span>
+ </button>
+ )}
</div>
<Link
className="relative w-[320px] aspect-video rounded-md overflow-hidden group"
href={`/en/anime/watch/${i.aniId}/${
i.provider
- }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}`}
+ }?id=${encodeURIComponent(i.watchId)}&num=${i.episode}${
+ i?.dub ? `&dub=${i?.dub}` : ""
+ }`}
>
<div className="w-full h-full bg-gradient-to-t from-black/70 from-20% to-transparent group-hover:to-black/40 transition-all duration-300 ease-out absolute z-30" />
<div className="absolute bottom-3 left-0 mx-2 text-white flex gap-2 items-center w-[80%] z-30">
@@ -372,8 +444,8 @@ export default function Content({
{i?.image && (
<Image
src={i?.image}
- width={200}
- height={200}
+ width="0"
+ height="0"
alt="Episode Thumbnail"
className="w-fit group-hover:scale-[1.02] duration-300 ease-out z-10"
/>
@@ -411,7 +483,7 @@ export default function Content({
section !== "Recommendations" && (
<div
key={section}
- className="flex cursor-pointer"
+ className="flex flex-col cursor-pointer"
onClick={goToPage}
>
<div className="w-[320px] aspect-video overflow-hidden object-cover rounded-md border-secondary border-2 flex flex-col gap-2 items-center text-center justify-center text-[#6a6a6a] hover:text-[#9f9f9f] hover:border-[#757575] transition-colors duration-200">
diff --git a/components/home/content/historyOptions.js b/components/home/content/historyOptions.js
new file mode 100644
index 0000000..1b9c5ed
--- /dev/null
+++ b/components/home/content/historyOptions.js
@@ -0,0 +1,56 @@
+import { Menu, Transition } from "@headlessui/react";
+import { XMarkIcon } from "@heroicons/react/24/outline";
+import React, { Fragment } from "react";
+
+export default function HistoryOptions({ remove, watchId, aniId }) {
+ return (
+ <Menu as="div" className="relative inline-block text-left">
+ <div>
+ <Menu.Button className="group/delete w-6 h-6 shrink-0 bg-primary p-1 rounded-full hover:text-action scale-100 hover:scale-105 transition-all duration-200 ease-out">
+ <XMarkIcon />
+ <span className="absolute font-karla bg-secondary shadow-black shadow-2xl py-1 px-2 whitespace-nowrap text-white text-sm rounded-md right-7 -bottom-[2px] z-40 duration-300 transition-all ease-out group-hover/delete:visible group-hover/delete:scale-100 group-hover/delete:translate-x-0 group-hover/delete:opacity-100 opacity-0 translate-x-10 scale-50 invisible">
+ Remove from history
+ </span>
+ </Menu.Button>
+ </div>
+ <Transition
+ as={Fragment}
+ enter="transition ease-out duration-100"
+ enterFrom="transform opacity-0 scale-95"
+ enterTo="transform opacity-100 scale-100"
+ leave="transition ease-in duration-75"
+ leaveFrom="transform opacity-100 scale-100"
+ leaveTo="transform opacity-0 scale-95"
+ >
+ <Menu.Items className="absolute z-50 right-0 mt-1 w-56 origin-top-right rounded-md bg-secondary shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
+ <div className="px-1 py-1 ">
+ <Menu.Item>
+ {({ active }) => (
+ <button
+ className={`${
+ active ? "bg-white/10 text-white" : "text-gray-100"
+ } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
+ onClick={() => remove(null, aniId)}
+ >
+ Delete All Episodes
+ </button>
+ )}
+ </Menu.Item>
+ <Menu.Item>
+ {({ active }) => (
+ <button
+ className={`${
+ active ? "bg-white/10 text-white" : "text-gray-100"
+ } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
+ onClick={() => remove(watchId, null)}
+ >
+ Delete Just This Episode
+ </button>
+ )}
+ </Menu.Item>
+ </div>
+ </Menu.Items>
+ </Transition>
+ </Menu>
+ );
+}
diff --git a/components/home/genres.js b/components/home/genres.js
index 3eefecd..f054fc9 100644
--- a/components/home/genres.js
+++ b/components/home/genres.js
@@ -55,7 +55,7 @@ export default function Genres() {
<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-[200px] left-0" />
+ <div className="bg-gradient-to-r from-primary to-transparent z-40 absolute w-7 h-full 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-4">
{g.map((a, index) => (
@@ -80,7 +80,7 @@ export default function Genres() {
))}
</div>
</div>
- <div className="bg-gradient-to-l from-primary to-transparent z-40 absolute w-7 h-[200px] lg:h-[300px] right-0" />
+ <div className="bg-gradient-to-l from-primary to-transparent z-40 absolute w-7 h-full right-0" />
</div>
</div>
);
diff --git a/components/home/mobileNav.js b/components/home/mobileNav.js
deleted file mode 100644
index 52c9d52..0000000
--- a/components/home/mobileNav.js
+++ /dev/null
@@ -1,202 +0,0 @@
-import { signIn, signOut } from "next-auth/react";
-import Link from "next/link";
-import { useState } from "react";
-
-export default function MobileNav({ sessions }) {
- const [isVisible, setIsVisible] = useState(false);
-
- const handleShowClick = () => {
- setIsVisible(true);
- };
-
- const handleHideClick = () => {
- setIsVisible(false);
- };
- return (
- <>
- {/* NAVBAR */}
- <div className="z-50">
- {!isVisible && (
- <button
- onClick={handleShowClick}
- className="fixed bottom-[30px] right-[20px] z-[100] flex h-[51px] w-[50px] cursor-pointer items-center justify-center rounded-[8px] bg-[#17171f] shadow-lg lg:hidden"
- id="bars"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- className="h-[42px] w-[61.5px] text-[#8BA0B2] fill-orange-500"
- viewBox="0 0 20 20"
- fill="currentColor"
- >
- <path
- fillRule="evenodd"
- d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
- clipRule="evenodd"
- />
- </svg>
- </button>
- )}
- </div>
-
- {/* Mobile Menu */}
- <div className={`transition-all duration-150 subpixel-antialiased z-50`}>
- {isVisible && sessions && (
- <Link
- href={`/en/profile/${sessions?.user.name}`}
- className="fixed lg:hidden bottom-[100px] w-[60px] h-[60px] flex items-center justify-center right-[20px] rounded-full z-50 bg-[#17171f]"
- >
- <img
- src={sessions?.user.image.large}
- alt="user avatar"
- className="object-cover w-[60px] h-[60px] rounded-full"
- />
- </Link>
- )}
- {isVisible && (
- <div className="fixed bottom-[30px] right-[20px] z-50 flex h-[51px] w-[300px] items-center justify-center gap-8 rounded-[8px] text-[11px] bg-[#17171f] shadow-lg lg:hidden">
- <div className="grid grid-cols-4 place-items-center gap-6">
- <button className="group flex flex-col items-center">
- <Link href="/en/">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
- />
- </svg>
- </Link>
- <Link
- href="/en/"
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- home
- </Link>
- </button>
- <button className="group flex flex-col items-center">
- <Link href="/en/about">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
- />
- </svg>
- </Link>
- <Link
- href="/en/about"
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- about
- </Link>
- </button>
- <button className="group flex gap-[1.5px] flex-col items-center ">
- <div>
- <Link href="/en/search/anime">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth={1.5}
- stroke="currentColor"
- className="w-6 h-6 group-hover:stroke-action"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
- />
- </svg>
- </Link>
- </div>
- <Link
- href="/en/search/anime"
- className="font-karla font-bold text-[#8BA0B2] group-hover:text-action"
- >
- search
- </Link>
- </button>
- {sessions ? (
- <button
- onClick={() => signOut("AniListProvider")}
- className="group flex gap-[1.5px] flex-col items-center "
- >
- <div>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 96 960 960"
- className="group-hover:fill-action w-6 h-6 fill-txt"
- >
- <path d="M186.666 936q-27 0-46.833-19.833T120 869.334V282.666q0-27 19.833-46.833T186.666 216H474v66.666H186.666v586.668H474V936H186.666zm470.668-176.667l-47-48 102-102H370v-66.666h341.001l-102-102 46.999-48 184 184-182.666 182.666z"></path>
- </svg>
- </div>
- <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action">
- logout
- </h1>
- </button>
- ) : (
- <button
- onClick={() => signIn("AniListProvider")}
- className="group flex gap-[1.5px] flex-col items-center "
- >
- <div>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 96 960 960"
- className="group-hover:fill-action w-6 h-6 fill-txt mr-2"
- >
- <path d="M486 936v-66.666h287.334V282.666H486V216h287.334q27 0 46.833 19.833T840 282.666v586.668q0 27-19.833 46.833T773.334 936H486zm-78.666-176.667l-47-48 102-102H120v-66.666h341l-102-102 47-48 184 184-182.666 182.666z"></path>
- </svg>
- </div>
- <h1 className="font-karla font-bold text-[#8BA0B2] group-hover:text-action">
- login
- </h1>
- </button>
- )}
- </div>
- <button onClick={handleHideClick}>
- <svg
- width="20"
- height="21"
- className="fill-orange-500"
- viewBox="0 0 20 21"
- fill="none"
- xmlns="http://www.w3.org/2000/svg"
- >
- <rect
- x="2.44043"
- y="0.941467"
- width="23.5842"
- height="3.45134"
- rx="1.72567"
- transform="rotate(45 2.44043 0.941467)"
- />
- <rect
- x="19.1172"
- y="3.38196"
- width="23.5842"
- height="3.45134"
- rx="1.72567"
- transform="rotate(135 19.1172 3.38196)"
- />
- </svg>
- </button>
- </div>
- )}
- </div>
- </>
- );
-}
diff --git a/components/home/recommendation.js b/components/home/recommendation.js
new file mode 100644
index 0000000..842932c
--- /dev/null
+++ b/components/home/recommendation.js
@@ -0,0 +1,91 @@
+import Image from "next/image";
+// import data from "../../assets/dummyData.json";
+import { BookOpenIcon, PlayIcon } from "@heroicons/react/24/solid";
+import { useDraggable } from "react-use-draggable-scroll";
+import { useRef } from "react";
+import Link from "next/link";
+
+export default function UserRecommendation({ data }) {
+ const ref = useRef(null);
+ const { events } = useDraggable(ref);
+
+ const uniqueRecommendationIds = new Set();
+
+ // Filter out duplicates from the recommendations array
+ const filteredData = data.filter((recommendation) => {
+ // Check if the ID is already in the set
+ if (uniqueRecommendationIds.has(recommendation.id)) {
+ // If it's a duplicate, return false to exclude it from the filtered array
+ return false;
+ }
+
+ // If it's not a duplicate, add the ID to the set and return true
+ uniqueRecommendationIds.add(recommendation.id);
+ return true;
+ });
+
+ return (
+ <div className="flex flex-col bg-tersier relative rounded overflow-hidden">
+ <div className="flex lg:gap-5 z-50">
+ <div className="flex flex-col items-start justify-center gap-3 lg:gap-7 lg:w-[50%] pl-5 lg:px-10">
+ <h2 className="font-bold text-3xl text-white">
+ {data[0].title.userPreferred}
+ </h2>
+ <p
+ dangerouslySetInnerHTML={{
+ __html: data[0].description?.replace(/<[^>]*>/g, ""),
+ }}
+ className="font-roboto font-light line-clamp-3 lg:line-clamp-3"
+ />
+ <button
+ type="button"
+ className="border border-white/70 py-1 px-2 lg:py-2 lg:px-4 rounded-full flex items-center gap-2 text-white font-bold"
+ >
+ {data[0].type === "ANIME" ? (
+ <PlayIcon className="w-5 h-5 text-white" />
+ ) : (
+ <BookOpenIcon className="w-5 h-5 text-white" />
+ )}
+ {data[0].type === "ANIME" ? "Watch" : "Read"} Now
+ </button>
+ </div>
+ <div
+ id="recommendation-list"
+ className="flex gap-5 overflow-x-scroll scrollbar-none px-5 py-7 lg:py-10"
+ ref={ref}
+ {...events}
+ >
+ {filteredData.slice(0, 9).map((i) => (
+ <Link
+ key={i.id}
+ href={`/en/${i.type.toLowerCase()}/${i.id}`}
+ className="relative snap-start shrink-0 group hover:bg-white/20 p-1 rounded"
+ >
+ <Image
+ src={i.coverImage.extraLarge}
+ alt={i.title.userPreferred}
+ width={190}
+ height={256}
+ className="h-[190px] w-[135px] lg:h-[265px] lg:w-[185px] rounded-md object-cover overflow-hidden transition-all duration-150 ease-in-out"
+ />
+ <span className="absolute rounded pointer-events-none w-[240px] h-[50%] transition-all duration-150 ease-in transform translate-x-[75%] group-hover:translate-x-[80%] top-0 left-0 bg-secondary opacity-0 group-hover:opacity-100 flex flex-col z-50">
+ <div className="">{i.title.userPreferred}</div>
+ <div>a</div>
+ </span>
+ </Link>
+ ))}
+ </div>
+ </div>
+ <div className="absolute top-0 left-0 z-40 bg-gradient-to-r from-transparent from-30% to-80% to-tersier w-[80%] lg:w-[60%] h-full" />
+ {data[0]?.bannerImage && (
+ <Image
+ src={data[0]?.bannerImage}
+ alt={data[0].title.userPreferred}
+ width={500}
+ height={500}
+ className="absolute top-0 left-0 z-30 w-[60%] h-full object-cover opacity-30"
+ />
+ )}
+ </div>
+ );
+}
diff --git a/components/home/schedule.js b/components/home/schedule.js
index 4043c5e..a9846a7 100644
--- a/components/home/schedule.js
+++ b/components/home/schedule.js
@@ -1,17 +1,23 @@
import Image from "next/image";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useState } from "react";
import { convertUnixToTime } from "../../utils/getTimes";
import { PlayIcon } from "@heroicons/react/20/solid";
import { BackwardIcon, ForwardIcon } from "@heroicons/react/24/solid";
import Link from "next/link";
+import { useCountdown } from "../../utils/useCountdownSeconds";
-export default function Schedule({ data, scheduleData, time }) {
+export default function Schedule({ data, scheduleData, anime, update }) {
let now = new Date();
let currentDay =
now.toLocaleString("default", { weekday: "long" }).toLowerCase() +
"Schedule";
currentDay = currentDay.replace("Schedule", "");
+ const [day, hours, minutes, seconds] = useCountdown(
+ anime[0]?.airingSchedule.nodes[0]?.airingAt * 1000 || Date.now(),
+ update
+ );
+
const [currentPage, setCurrentPage] = useState(0);
const [days, setDay] = useState();
@@ -37,8 +43,6 @@ export default function Schedule({ data, scheduleData, time }) {
setCurrentPage(todayIndex >= 0 ? todayIndex : 0);
}, [currentDay, days]);
- // console.log({ scheduleData });
-
return (
<div className="flex flex-col gap-5 px-4 lg:px-0">
<h1 className="font-bold font-karla text-[20px] lg:px-5">
@@ -46,7 +50,7 @@ export default function Schedule({ data, scheduleData, time }) {
</h1>
<div className="rounded mb-5 shadow-md shadow-black">
<div className="overflow-hidden w-full h-[96px] lg:h-[10rem] rounded relative">
- <div className="absolute flex flex-col justify-center pl-5 lg:pl-16 rounded z-20 bg-gradient-to-r from-30% from-[#0c0c0c] to-transparent w-full h-full">
+ <div className="absolute flex flex-col justify-center pl-5 lg:pl-16 rounded z-20 bg-gradient-to-r from-30% from-tersier to-transparent w-full h-full">
<h1 className="text-xs lg:text-lg">Coming Up Next!</h1>
<div className="w-1/2 lg:w-2/5 hidden lg:block font-medium font-karla leading-[2.9rem] text-white line-clamp-1">
<Link
@@ -62,15 +66,15 @@ export default function Schedule({ data, scheduleData, time }) {
</div>
{data.bannerImage ? (
<Image
- src={data.bannerImage || data.coverImage.large}
+ src={data.bannerImage || data.coverImage.extraLarge}
width={500}
height={500}
alt="banner next anime"
- className="absolute z-10 top-0 right-0 w-3/4 h-full object-cover brightness-[30%]"
+ className="absolute z-10 top-0 right-0 w-3/4 h-full object-cover opacity-30"
/>
) : (
<Image
- src={data.coverImage.large}
+ src={data.coverImage.extraLarge}
width={500}
height={500}
sizes="100vw"
@@ -87,22 +91,22 @@ export default function Schedule({ data, scheduleData, time }) {
<div className="flex items-center gap-2 md:gap-5 font-bold font-karla text-sm md:text-xl">
{/* Countdown Timer */}
<div className="flex flex-col items-center">
- <span className="text-action/80">{time.days}</span>
+ <span className="text-action/80">{day}</span>
<span className="text-sm lg:text-base font-medium">Days</span>
</div>
<span></span>
<div className="flex flex-col items-center">
- <span className="text-action/80">{time.hours}</span>
+ <span className="text-action/80">{hours}</span>
<span className="text-sm lg:text-base font-medium">Hours</span>
</div>
<span></span>
<div className="flex flex-col items-center">
- <span className="text-action/80">{time.minutes}</span>
+ <span className="text-action/80">{minutes}</span>
<span className="text-sm lg:text-base font-medium">Mins</span>
</div>
<span></span>
<div className="flex flex-col items-center">
- <span className="text-action/80">{time.seconds}</span>
+ <span className="text-action/80">{seconds}</span>
<span className="text-sm lg:text-base font-medium">Secs</span>
</div>
</div>
diff --git a/components/home/staticNav.js b/components/home/staticNav.js
index 93f7b26..b22a9e3 100644
--- a/components/home/staticNav.js
+++ b/components/home/staticNav.js
@@ -1,51 +1,27 @@
-import { signIn, useSession } from "next-auth/react";
-import { useRouter } from "next/router";
-import { useEffect, useState } from "react";
+import { signIn, signOut, useSession } from "next-auth/react";
import { getCurrentSeason } from "../../utils/getTimes";
import Link from "next/link";
-import { parseCookies } from "nookies";
+// import { } from "@heroicons/react/24/solid";
+import { useSearch } from "../../lib/hooks/isOpenState";
+import Image from "next/image";
+import { UserIcon } from "@heroicons/react/20/solid";
+import { useRouter } from "next/router";
export default function Navigasi() {
const { data: sessions, status } = useSession();
- const [year, setYear] = useState(new Date().getFullYear());
- const [season, setSeason] = useState(getCurrentSeason());
-
- const [lang, setLang] = useState("en");
- const [cookie, setCookies] = useState(null);
+ const year = new Date().getFullYear();
+ const season = getCurrentSeason();
const router = useRouter();
- useEffect(() => {
- let lang = null;
- if (!cookie) {
- const cookie = parseCookies();
- lang = cookie.lang || null;
- setCookies(cookie);
- }
- if (lang === "en" || lang === null) {
- setLang("en");
- } else if (lang === "id") {
- setLang("id");
- }
- }, []);
+ const { setIsOpen } = useSearch();
- const handleFormSubmission = (inputValue) => {
- router.push(`/${lang}/search/${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 lg:mx-[94px]">
- <div className="flex items-center lg:gap-16 lg:pt-7">
+ <div className="flex w-full items-center justify-between px-5 lg:mx-[94px] lg:pt-7">
+ <div className="flex items-center lg:gap-16">
<Link
href="/en/"
className=" font-outfit lg:text-[40px] text-[30px] font-bold text-[#FF7F57]"
@@ -55,16 +31,35 @@ export default function Navigasi() {
<ul className="hidden items-center gap-10 pt-2 font-outfit text-[14px] lg:flex">
<li>
<Link
- href={`/en/search/anime?season=${season}&seasonYear=${year}`}
+ href={`/en/search/anime?season=${season}&year=${year}`}
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
>
This Season
</Link>
</li>
<li>
- <Link href="/en/search/manga">Manga</Link>
+ <Link
+ href="/en/search/manga"
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
+ >
+ Manga
+ </Link>
</li>
<li>
- <Link href="/en/search/anime">Anime</Link>
+ <Link
+ href="/en/search/anime"
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
+ >
+ Anime
+ </Link>
+ </li>
+ <li>
+ <Link
+ href="/en/schedule"
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
+ >
+ Schedule
+ </Link>
</li>
{status === "loading" ? (
@@ -75,15 +70,19 @@ export default function Navigasi() {
<li>
<button
onClick={() => signIn("AniListProvider")}
- className="ring-1 ring-action font-karla font-bold px-2 py-1 rounded-md"
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
+ // className="px-2 py-1 ring-1 ring-action font-bold font-karla rounded-md"
>
- Sign in
+ Sign In
</button>
</li>
)}
{sessions && (
<li className="text-center">
- <Link href={`/en/profile/${sessions?.user.name}`}>
+ <Link
+ href={`/en/profile/${sessions?.user.name}`}
+ className="hover:text-action/80 transition-all duration-150 ease-linear"
+ >
My List
</Link>
</li>
@@ -92,18 +91,73 @@ export default function Navigasi() {
)}
</ul>
</div>
- <div className="relative flex lg:scale-75 scale-[65%] items-center mb-7 lg:mb-1">
- <div className="search-box ">
- <input
- className="search-text"
- type="text"
- placeholder="Search Anime"
- onKeyDown={handleKeyDown}
- />
- <div className="search-btn">
- <i className="fas fa-search"></i>
- </div>
- </div>
+ <div className="flex items-center gap-4">
+ <button
+ type="button"
+ onClick={() => setIsOpen(true)}
+ className="flex-center w-[26px] h-[26px]"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="32"
+ height="32"
+ viewBox="0 0 24 24"
+ >
+ <path
+ fill="none"
+ stroke="currentColor"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ strokeWidth="2"
+ d="M15 15l6 6m-11-4a7 7 0 110-14 7 7 0 010 14z"
+ ></path>
+ </svg>
+ </button>
+ {/* <div
+ className="bg-white"
+ // title={sessions ? "Go to Profile" : "Login With AniList"}
+ > */}
+ {sessions ? (
+ <button
+ type="button"
+ onClick={() =>
+ router.push(`/en/profile/${sessions?.user.name}`)
+ }
+ className="w-7 h-7 relative flex flex-col items-center group"
+ >
+ <Image
+ src={sessions?.user.image.large}
+ alt="avatar"
+ width={50}
+ height={50}
+ className="w-full h-full object-cover"
+ />
+ <div className="hidden absolute z-50 w-28 text-center -bottom-20 text-white shadow-2xl opacity-0 bg-secondary p-1 py-2 rounded-md font-karla font-light invisible group-hover:visible group-hover:opacity-100 duration-300 transition-all md:grid place-items-center gap-1">
+ <Link
+ href={`/en/profile/${sessions?.user.name}`}
+ className="hover:text-action"
+ >
+ Profile
+ </Link>
+ <div
+ onClick={() => signOut({ callbackUrl: "/" })}
+ className="hover:text-action cursor-pointer"
+ >
+ Log out
+ </div>
+ </div>
+ </button>
+ ) : (
+ <button
+ type="button"
+ onClick={() => signIn("AniListProvider")}
+ title="Login With AniList"
+ className="w-7 h-7 bg-white/30 rounded-full overflow-hidden"
+ >
+ <UserIcon className="w-full h-full translate-y-2 text-white/50" />
+ </button>
+ )}
+ {/* </div> */}
</div>
</div>
</div>