aboutsummaryrefslogtreecommitdiff
path: root/components/anime/mobile
diff options
context:
space:
mode:
authorFactiven <[email protected]>2023-12-24 13:03:54 +0700
committerFactiven <[email protected]>2023-12-24 13:03:54 +0700
commit50a0f0240d7fef133eb5acc1bea2b1168b08e9db (patch)
tree307e09e505580415a58d64b5fc3580e9235869f1 /components/anime/mobile
parentUpdate README.md (#104) (diff)
downloadmoopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.tar.xz
moopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.zip
migrate to typescript
Diffstat (limited to 'components/anime/mobile')
-rw-r--r--components/anime/mobile/reused/infoChip.tsx (renamed from components/anime/mobile/reused/infoChip.js)23
-rw-r--r--components/anime/mobile/topSection.tsx (renamed from components/anime/mobile/topSection.js)325
2 files changed, 209 insertions, 139 deletions
diff --git a/components/anime/mobile/reused/infoChip.js b/components/anime/mobile/reused/infoChip.tsx
index 41def85..80ebf83 100644
--- a/components/anime/mobile/reused/infoChip.js
+++ b/components/anime/mobile/reused/infoChip.tsx
@@ -1,7 +1,20 @@
-import React from "react";
-import { getFormat } from "../../../../utils/getFormat";
+import React, { CSSProperties, FC } from "react";
+import { getFormat } from "@/utils/getFormat";
-export default function InfoChip({ info, color, className }) {
+interface Info {
+ episodes?: number;
+ averageScore?: number;
+ format?: string;
+ status?: string;
+}
+
+interface InfoChipProps {
+ info: Info;
+ color: any;
+ className: string;
+}
+
+const InfoChip: FC<InfoChipProps> = ({ info, color, className }) => {
return (
<div
className={`flex-wrap w-full justify-start md:pt-1 gap-4 ${className}`}
@@ -40,4 +53,6 @@ export default function InfoChip({ info, color, className }) {
)}
</div>
);
-}
+};
+
+export default InfoChip;
diff --git a/components/anime/mobile/topSection.js b/components/anime/mobile/topSection.tsx
index 6780da5..2d28c66 100644
--- a/components/anime/mobile/topSection.js
+++ b/components/anime/mobile/topSection.tsx
@@ -11,26 +11,36 @@ import { convertSecondsToTime } from "@/utils/getTimes";
import Link from "next/link";
import InfoChip from "./reused/infoChip";
import Description from "./reused/description";
-import { NewNavbar } from "@/components/shared/NavBar";
+import Skeleton from "react-loading-skeleton";
+import { AniListInfoTypes } from "types/info/AnilistInfoTypes";
+
+type DetailTopProps = {
+ info?: AniListInfoTypes | null;
+ statuses?: any;
+ handleOpen: () => void;
+ watchUrl: string | undefined;
+ progress?: number;
+ color?: string | null;
+};
export default function DetailTop({
info,
- statuses,
+ statuses = undefined,
handleOpen,
watchUrl,
progress,
color,
-}) {
+}: DetailTopProps) {
const router = useRouter();
const [readMore, setReadMore] = useState(false);
const [showAll, setShowAll] = useState(false);
- const isAnime = info.type === "ANIME";
+ const isAnime = info?.type === "ANIME";
useEffect(() => {
setReadMore(false);
- }, [info.id]);
+ }, [info?.id]);
const handleShareClick = async () => {
try {
@@ -51,78 +61,150 @@ export default function DetailTop({
return (
<div className="gap-6 w-full px-3 pt-4 md:pt-10 flex flex-col items-center justify-center">
- <NewNavbar info={info} />
-
{/* MAIN */}
<div className="flex flex-col md:flex-row w-full items-center md:items-end gap-5 pt-12">
<div className="shrink-0 w-[180px] h-[250px] rounded overflow-hidden">
- <img
- src={info?.coverImage?.extraLarge || info?.coverImage}
- alt="poster anime"
- width={300}
- height={300}
- className="w-full h-full object-cover"
- />
+ {info ? (
+ <img
+ src={
+ info?.coverImage?.extraLarge?.toString() ??
+ info?.coverImage?.toString()
+ }
+ alt="poster anime"
+ width={300}
+ height={300}
+ className="w-full h-full object-cover"
+ />
+ ) : (
+ <Skeleton className="h-full" />
+ )}
</div>
<div className="flex flex-col gap-4 items-center md:items-start justify-end w-full">
- <div className="flex flex-col gap-1 text-center md:text-start">
+ <div className="flex flex-col gap-1 text-center md:text-start w-full">
<h3 className="font-karla text-lg capitalize leading-none">
{info?.season?.toLowerCase() || getMonth(info?.startDate?.month)}{" "}
- {info.seasonYear || info?.startDate?.year}
+ {info?.seasonYear || info?.startDate?.year}
+ {!info && <Skeleton height={14} width={140} />}
</h3>
<h1 className="font-outfit font-extrabold text-2xl md:text-4xl line-clamp-2 text-white">
{info?.title?.romaji || info?.title?.english}
+ {!info && <Skeleton height={35} width={340} className="" />}
</h1>
<h2 className="font-karla line-clamp-1 text-sm md:text-lg md:pb-2 font-light text-white/70">
- {info.title?.english}
+ {info?.title?.english}
</h2>
- <InfoChip info={info} color={color} className="hidden md:flex" />
- {info?.description && (
- <Description
- info={info}
- readMore={readMore}
- setReadMore={setReadMore}
- className="md:block hidden"
- />
+ {info && (
+ <InfoChip info={info} color={color} className="hidden md:flex" />
+ )}
+ {info ? (
+ info?.description && (
+ <Description
+ info={info}
+ readMore={readMore}
+ setReadMore={setReadMore}
+ className="md:block hidden"
+ />
+ )
+ ) : (
+ <div className="w-full md:px-0 md:block hidden">
+ <Skeleton className="h-[80px] w-[700px]" />
+ </div>
)}
</div>
</div>
</div>
<div className="hidden md:flex gap-5 items-center justify-start w-full">
- <button
- type="button"
- onClick={() => router.push(watchUrl)}
- className={`${
- !watchUrl ? "opacity-30 pointer-events-none" : ""
- } w-[180px] flex-center text-lg font-karla font-semibold gap-2 border-black border-opacity-10 text-black rounded-full py-1 px-4 bg-white hover:opacity-80`}
- >
- {isAnime ? (
- <PlayIcon className="w-5 h-5" />
- ) : (
- <BookOpenIcon className="w-5 h-5" />
- )}
- {progress > 0 ? (
- statuses?.value === "COMPLETED" ? (
- isAnime ? (
- "Rewatch"
+ {info ? (
+ <button
+ type="button"
+ onClick={() => router.push(watchUrl ?? "#")}
+ className={`${
+ !watchUrl ? "opacity-30 pointer-events-none" : ""
+ } w-[180px] flex-center text-lg font-karla font-semibold gap-2 border-black border-opacity-10 text-black rounded-full py-1 px-4 bg-white hover:opacity-80`}
+ >
+ {isAnime ? (
+ <PlayIcon className="w-5 h-5" />
+ ) : (
+ <BookOpenIcon className="w-5 h-5" />
+ )}
+ {progress && progress > 0 ? (
+ statuses?.value === "COMPLETED" ? (
+ isAnime ? (
+ "Rewatch"
+ ) : (
+ "Reread"
+ )
+ ) : !watchUrl && info?.nextAiringEpisode ? (
+ <span>
+ {convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
+ </span>
) : (
- "Reread"
+ "Continue"
)
- ) : !watchUrl && info?.nextAiringEpisode ? (
- <span>
- {convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
- </span>
+ ) : isAnime ? (
+ "Watch Now"
) : (
- "Continue"
- )
- ) : isAnime ? (
- "Watch Now"
+ "Read Now"
+ )}
+ </button>
+ ) : (
+ <div className="h-10 w-[180px] bg-secondary flex-center text-lg font-karla font-semibold gap-2 border-black border-opacity-10 text-black rounded-full" />
+ )}
+ <div className="flex gap-2">
+ {info ? (
+ <button
+ type="button"
+ className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
+ onClick={() => handleOpen()}
+ >
+ <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
+ Add to List
+ </span>
+ <PlusIcon className="w-5 h-5" />
+ </button>
) : (
- "Read Now"
+ <div className="w-10 h-10 bg-secondary rounded-full" />
)}
- </button>
- <div className="flex gap-2">
+ {info ? (
+ <button
+ type="button"
+ className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
+ onClick={handleShareClick}
+ >
+ <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
+ Share {isAnime ? "Anime" : "Manga"}
+ </span>
+ <ShareIcon className="w-5 h-5" />
+ </button>
+ ) : (
+ <div className="w-10 h-10 bg-secondary rounded-full" />
+ )}
+ {info ? (
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href={`https://anilist.co/${info.type.toLowerCase()}/${info.id}`}
+ className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
+ >
+ <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
+ See on AniList
+ </span>
+ <Image
+ src="/svg/anilist-icon.svg"
+ alt="anilist_icon"
+ width={20}
+ height={20}
+ />
+ </a>
+ ) : (
+ <div className="w-10 h-10 bg-secondary rounded-full" />
+ )}
+ </div>
+ </div>
+
+ <div className="md:hidden flex gap-2 items-center justify-center w-[90%]">
+ {info ? (
<button
type="button"
className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
@@ -133,6 +215,46 @@ export default function DetailTop({
</span>
<PlusIcon className="w-5 h-5" />
</button>
+ ) : (
+ <div className="w-10 h-10 bg-secondary rounded-full" />
+ )}
+ {info ? (
+ <button
+ type="button"
+ onClick={() => router.push(watchUrl ?? "#")}
+ className={`${
+ !watchUrl ? "opacity-30 pointer-events-none" : ""
+ } flex items-center text-lg font-karla font-semibold gap-1 border-black border-opacity-10 text-black rounded-full py-2 px-4 bg-white`}
+ >
+ {isAnime ? (
+ <PlayIcon className="w-5 h-5" />
+ ) : (
+ <BookOpenIcon className="w-5 h-5" />
+ )}
+ {progress && progress > 0 ? (
+ statuses?.value === "COMPLETED" ? (
+ isAnime ? (
+ "Rewatch"
+ ) : (
+ "Reread"
+ )
+ ) : !watchUrl && info?.nextAiringEpisode ? (
+ <span>
+ {convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
+ </span>
+ ) : (
+ "Continue"
+ )
+ ) : isAnime ? (
+ "Watch Now"
+ ) : (
+ "Read Now"
+ )}
+ </button>
+ ) : (
+ <div className="h-10 w-32 bg-secondary flex-center text-lg font-karla font-semibold gap-2 border-black border-opacity-10 text-black rounded-full" />
+ )}
+ {info ? (
<button
type="button"
className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
@@ -143,81 +265,12 @@ export default function DetailTop({
</span>
<ShareIcon className="w-5 h-5" />
</button>
- <a
- target="_blank"
- rel="noopener noreferrer"
- href={`https://anilist.co/${info.type.toLowerCase()}/${info.id}`}
- className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
- >
- <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
- See on AniList
- </span>
- <Image
- src="/svg/anilist-icon.svg"
- alt="anilist_icon"
- width={20}
- height={20}
- />
- </a>
- </div>
+ ) : (
+ <div className="w-10 h-10 bg-secondary rounded-full" />
+ )}
</div>
- <div className="md:hidden flex gap-2 items-center justify-center w-[90%]">
- <button
- type="button"
- className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
- onClick={() => handleOpen()}
- >
- <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
- Add to List
- </span>
- <PlusIcon className="w-5 h-5" />
- </button>
- <button
- type="button"
- onClick={() => router.push(watchUrl)}
- className={`${
- !watchUrl ? "opacity-30 pointer-events-none" : ""
- } flex items-center text-lg font-karla font-semibold gap-1 border-black border-opacity-10 text-black rounded-full py-2 px-4 bg-white`}
- >
- {isAnime ? (
- <PlayIcon className="w-5 h-5" />
- ) : (
- <BookOpenIcon className="w-5 h-5" />
- )}
- {progress > 0 ? (
- statuses?.value === "COMPLETED" ? (
- isAnime ? (
- "Rewatch"
- ) : (
- "Reread"
- )
- ) : !watchUrl && info?.nextAiringEpisode ? (
- <span>
- {convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring)}{" "}
- </span>
- ) : (
- "Continue"
- )
- ) : isAnime ? (
- "Watch Now"
- ) : (
- "Read Now"
- )}
- </button>
- <button
- type="button"
- className="flex-center group relative w-10 h-10 bg-secondary rounded-full"
- onClick={handleShareClick}
- >
- <span className="absolute pointer-events-none z-40 opacity-0 -translate-y-8 group-hover:-translate-y-10 group-hover:opacity-100 font-karla shadow-tersier shadow-md whitespace-nowrap bg-secondary px-2 py-1 rounded transition-all duration-200 ease-out">
- Share {isAnime ? "Anime" : "Manga"}
- </span>
- <ShareIcon className="w-5 h-5" />
- </button>
- </div>
-
- {info.nextAiringEpisode?.timeUntilAiring && (
+ {info && info.nextAiringEpisode?.timeUntilAiring && (
<p className="md:hidden">
Episode {info.nextAiringEpisode.episode} in{" "}
<span className="font-bold">
@@ -226,7 +279,7 @@ export default function DetailTop({
</p>
)}
- {info?.description && (
+ {info && info?.description && (
<Description
info={info}
readMore={readMore}
@@ -235,13 +288,15 @@ export default function DetailTop({
/>
)}
- <InfoChip
- info={info}
- color={color}
- className={`${readMore ? "flex" : "hidden"} md:hidden`}
- />
+ {info && (
+ <InfoChip
+ info={info}
+ color={color}
+ className={`${readMore ? "flex" : "hidden"} md:hidden`}
+ />
+ )}
- {info?.relations?.edges?.length > 0 && (
+ {info && info?.relations?.edges?.length > 0 && (
<div className="w-screen md:w-full">
<div className="flex justify-between items-center p-3 md:p-0">
{info?.relations?.edges?.length > 0 && (
@@ -288,7 +343,7 @@ export default function DetailTop({
<div className="w-[90px] bg-image rounded-l-md shrink-0">
<Image
src={rel.coverImage.extraLarge}
- alt={rel.id}
+ alt={rel.id.toString()}
height={500}
width={500}
className="object-cover h-full w-full shrink-0 rounded-l-md"
@@ -314,7 +369,7 @@ export default function DetailTop({
);
}
-function getMonth(month) {
+function getMonth(month: number | undefined) {
if (!month) return "";
const formattedMonth = new Date(0, month).toLocaleString("default", {
month: "long",