diff options
| author | Artrix <[email protected]> | 2024-01-05 05:12:52 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-01-05 20:12:52 +0700 |
| commit | 553fe1c71082b040e9f9667ad3e99acdb33990b2 (patch) | |
| tree | 0c770c406c8ff934ce34d8b10dbae948a554a619 /components | |
| parent | migrate to typescript (diff) | |
| download | moopa-553fe1c71082b040e9f9667ad3e99acdb33990b2.tar.xz moopa-553fe1c71082b040e9f9667ad3e99acdb33990b2.zip | |
feat: Implement a way to review/rate anime (#108)
* Make details cover lead back to anime page
* Make 'markProgress' use object instead of param list
* Import Link
* Implement Rate modal
* Pass session into useAniList
Co-authored-by: Factiven <[email protected]>
* Reimplement using markComplete & add toast for failure
* redefined ratemodal
* fix: home page client error
* update version
---------
Co-authored-by: Factiven <[email protected]>
Diffstat (limited to 'components')
| -rw-r--r-- | components/anime/mobile/topSection.tsx | 2 | ||||
| -rw-r--r-- | components/home/schedule.js | 4 | ||||
| -rw-r--r-- | components/listEditor.tsx | 2 | ||||
| -rw-r--r-- | components/manga/panels/firstPanel.js | 18 | ||||
| -rw-r--r-- | components/manga/panels/secondPanel.js | 26 | ||||
| -rw-r--r-- | components/manga/panels/thirdPanel.js | 16 | ||||
| -rw-r--r-- | components/manga/rightBar.js | 2 | ||||
| -rw-r--r-- | components/shared/RateModal.tsx | 135 | ||||
| -rw-r--r-- | components/shared/changelogs.tsx | 78 | ||||
| -rw-r--r-- | components/watch/new-player/components/layouts/video-layout.tsx | 23 | ||||
| -rw-r--r-- | components/watch/new-player/player.tsx | 25 | ||||
| -rw-r--r-- | components/watch/primary/details.tsx | 21 |
12 files changed, 261 insertions, 91 deletions
diff --git a/components/anime/mobile/topSection.tsx b/components/anime/mobile/topSection.tsx index 2d28c66..b5d4f62 100644 --- a/components/anime/mobile/topSection.tsx +++ b/components/anime/mobile/topSection.tsx @@ -65,7 +65,7 @@ export default function DetailTop({ <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"> {info ? ( - <img + <Image src={ info?.coverImage?.extraLarge?.toString() ?? info?.coverImage?.toString() diff --git a/components/home/schedule.js b/components/home/schedule.js index 19260c2..df61eba 100644 --- a/components/home/schedule.js +++ b/components/home/schedule.js @@ -13,11 +13,13 @@ export default function Schedule({ data, scheduleData, anime, update }) { "Schedule"; currentDay = currentDay.replace("Schedule", ""); - const { day, hours, minutes, seconds } = useCountdown( + const { countdown } = useCountdown( anime[0]?.airingSchedule.nodes[0]?.airingAt * 1000 || Date.now(), update ); + const {days: day, hours, minutes, seconds} = countdown; + const [currentPage, setCurrentPage] = useState(0); const [days, setDay] = useState(); diff --git a/components/listEditor.tsx b/components/listEditor.tsx index 2e180a1..045e254 100644 --- a/components/listEditor.tsx +++ b/components/listEditor.tsx @@ -2,7 +2,7 @@ import { useState, FormEvent } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; import { toast } from "sonner"; -import { AniListInfoTypes } from "@/types/info/AnilistInfoTypes"; +import { AniListInfoTypes } from "types/info/AnilistInfoTypes"; interface ListEditorProps { animeId: number; diff --git a/components/manga/panels/firstPanel.js b/components/manga/panels/firstPanel.js index 8470fd0..0ceb2fb 100644 --- a/components/manga/panels/firstPanel.js +++ b/components/manga/panels/firstPanel.js @@ -66,13 +66,13 @@ export default function FirstPanel({ if (session) { if (aniId?.length > 6) return; const currentChapter = chapter.chapters?.find( - (x) => x.id === currentId + (x) => x.id === currentId, ); if (currentChapter) { const chapterNumber = currentChapter.number ?? chapter.chapters.indexOf(currentChapter) + 1; - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); console.log("marking progress"); } } @@ -142,14 +142,14 @@ export default function FirstPanel({ > <Image src={`https://aoi.moopa.live/utils/image-proxy?url=${encodeURIComponent( - i.url + i.url, )}${ i?.headers?.Referer ? `&headers=${encodeURIComponent( - JSON.stringify(i?.headers) + JSON.stringify(i?.headers), )}` : `&headers=${encodeURIComponent( - JSON.stringify(getHeaders(chapter.providerId)) + JSON.stringify(getHeaders(chapter.providerId)), )}` }`} alt={index} @@ -213,10 +213,10 @@ export default function FirstPanel({ `/en/manga/read/${ chapter.providerId }?id=${mangadexId}&chapterId=${encodeURIComponent( - prevChapter?.id + prevChapter?.id, )}${aniId?.length > 6 ? "" : `&anilist=${aniId}`}&num=${ prevChapter?.number - }` + }`, ) } > @@ -234,10 +234,10 @@ export default function FirstPanel({ `/en/manga/read/${ chapter.providerId }?id=${mangadexId}&chapterId=${encodeURIComponent( - nextChapter?.id + nextChapter?.id, )}${aniId?.length > 6 ? "" : `&anilist=${aniId}`}&num=${ nextChapter?.number - }` + }`, ) } > diff --git a/components/manga/panels/secondPanel.js b/components/manga/panels/secondPanel.js index 23a9da0..6ebc292 100644 --- a/components/manga/panels/secondPanel.js +++ b/components/manga/panels/secondPanel.js @@ -69,12 +69,12 @@ export default function SecondPanel({ if (index + 1 >= image.length - 4 && !hasRun.current) { const current = chapterData.chapters?.find( - (x) => x.id === currentChapter.id + (x) => x.id === currentChapter.id, ); const chapterNumber = chapterData.chapters.indexOf(current) + 1; if (chapterNumber) { - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); } hasRun.current = true; } @@ -98,15 +98,15 @@ export default function SecondPanel({ if (index + 1 >= image.length - 4 && !hasRun.current) { console.log("marking progress"); const current = chapterData.chapters?.find( - (x) => x.id === currentChapter.id + (x) => x.id === currentChapter.id, ); const chapterNumber = chapterData.chapters.indexOf(current) + 1; if (chapterNumber) { - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); } - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); hasRun.current = true; } }; @@ -137,16 +137,16 @@ export default function SecondPanel({ height={500} className="w-1/2 h-screen object-contain" src={`https://aoi.moopa.live/utils/image-proxy?url=${encodeURIComponent( - image[image.length - index - 2]?.url + image[image.length - index - 2]?.url, )}${ image[image.length - index - 2]?.headers?.Referer ? `&headers=${encodeURIComponent( JSON.stringify( - image[image.length - index - 2]?.headers - ) + image[image.length - index - 2]?.headers, + ), )}` : `&headers=${encodeURIComponent( - JSON.stringify(getHeaders(providerId)) + JSON.stringify(getHeaders(providerId)), )}` }`} alt="Manga Page" @@ -158,14 +158,16 @@ export default function SecondPanel({ height={500} className="w-1/2 h-screen object-contain" src={`https://aoi.moopa.live/utils/image-proxy?url=${encodeURIComponent( - image[image.length - index - 1]?.url + image[image.length - index - 1]?.url, )}${ image[image.length - index - 1]?.headers?.Referer ? `&headers=${encodeURIComponent( - JSON.stringify(image[image.length - index - 1]?.headers) + JSON.stringify( + image[image.length - index - 1]?.headers, + ), )}` : `&headers=${encodeURIComponent( - JSON.stringify(getHeaders(providerId)) + JSON.stringify(getHeaders(providerId)), )}` }`} alt="Manga Page" diff --git a/components/manga/panels/thirdPanel.js b/components/manga/panels/thirdPanel.js index 77bb132..7c43f6e 100644 --- a/components/manga/panels/thirdPanel.js +++ b/components/manga/panels/thirdPanel.js @@ -66,12 +66,12 @@ export default function ThirdPanel({ } if (index + 1 >= image.length - 2 && !hasRun.current) { const current = chapterData.chapters?.find( - (x) => x.id === currentChapter.id + (x) => x.id === currentChapter.id, ); const chapterNumber = chapterData.chapters.indexOf(current) + 1; if (chapterNumber) { - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); } hasRun.current = true; } @@ -94,12 +94,12 @@ export default function ThirdPanel({ } if (index + 1 >= image.length - 2 && !hasRun.current) { const current = chapterData.chapters?.find( - (x) => x.id === currentChapter.id + (x) => x.id === currentChapter.id, ); const chapterNumber = chapterData.chapters.indexOf(current) + 1; if (chapterNumber) { - markProgress(aniId, chapterNumber); + markProgress({ mediaId: aniId, progress: chapterNumber }); } hasRun.current = true; @@ -128,14 +128,16 @@ export default function ThirdPanel({ className="w-full h-screen object-contain" onClick={() => setMobileVisible(!mobileVisible)} src={`https://aoi.moopa.live/utils/image-proxy?url=${encodeURIComponent( - image[image.length - index - 1]?.url + image[image.length - index - 1]?.url, )}${ image[image.length - index - 1]?.headers?.Referer ? `&headers=${encodeURIComponent( - JSON.stringify(image[image.length - index - 1]?.headers) + JSON.stringify( + image[image.length - index - 1]?.headers, + ), )}` : `&headers=${encodeURIComponent( - JSON.stringify(getHeaders(providerId)) + JSON.stringify(getHeaders(providerId)), )}` }`} alt="Manga Page" diff --git a/components/manga/rightBar.js b/components/manga/rightBar.js index 9672fc4..3da04d9 100644 --- a/components/manga/rightBar.js +++ b/components/manga/rightBar.js @@ -43,7 +43,7 @@ export default function RightBar({ parsedProgress === parseInt(parsedProgress) && parsedVolumeProgress === parseInt(parsedVolumeProgress) ) { - markProgress(id, progress, status, volumeProgress); + markProgress({ mediaId: id, progress, stats: status, volumeProgress }); hasRun.current = true; } else { toast.error("Progress must be a whole number!"); diff --git a/components/shared/RateModal.tsx b/components/shared/RateModal.tsx new file mode 100644 index 0000000..6231eaf --- /dev/null +++ b/components/shared/RateModal.tsx @@ -0,0 +1,135 @@ +import { useAniList } from "@/lib/anilist/useAnilist"; +import { useWatchProvider } from "@/lib/context/watchPageProvider"; +import { useState } from "react"; +import { toast } from "sonner"; + +type Props = { + toggle: boolean; + position: "top" | "bottom"; + setToggle: (prev: any) => void; + session: any; +}; + +export default function RateModal({ + toggle, + position, + setToggle, + session, +}: Props) { + const [startRate, setStartRate] = useState(false); + const { markComplete } = useAniList(session); + + const { dataMedia } = useWatchProvider(); + + async function handleSubmit(event: any) { + event.preventDefault(); + const data = new FormData(event.target); + const rating = data.get("rating"); + const notes = data.get("notes"); + try { + await markComplete(dataMedia?.id, { notes, scoreRaw: rating }); + toast.success("Successfully rated!"); + setToggle((prev: any) => { + return { + ...prev, + isOpen: false, + }; + }); + } catch (error) { + toast.error("Failed to rate!"); + } + } + + function handleClose() { + setToggle((prev: any) => { + return { + ...prev, + isOpen: false, + }; + }); + } + return ( + <> + <div + className={`w-full h-[20dvh] fixed bg-gradient-to-${ + position === "top" + ? `b top-0 from-black/20` + : "t -bottom-5 from-black/40" + } to-transparent z-10 transition-all duration-200 ease-in-out ${ + toggle ? "" : "opacity-0 pointer-events-none" + }`} + /> + <div + style={{ width: startRate ? "300px" : "240px" }} + className={`${ + position === "top" + ? toggle + ? `top-5` + : `-top-48` + : toggle + ? `bottom-10` + : `-bottom-48` + } fixed text-white font-semibold z-50 font-karla transition-all duration-300 ease-in-out left-1/2 transform -translate-x-1/2 bg-secondary p-3 rounded flex flex-col justify-center items-center gap-4`} + > + <p className="text-lg">What do you think?</p> + <div + className={`flex gap-2 font-medium text-center transition-all duration-200 ${ + startRate + ? "scale-50 hidden pointer-events-none" + : "scale-100 opacity-100" + }`} + > + <button + onClick={() => setStartRate(true)} + className="w-[100px] py-1 bg-action/10 rounded text-action" + > + Rate Now + </button> + <button + onClick={handleClose} + className="w-[100px] py-1 border border-opacity-0 hover:border-opacity-10 rounded border-white" + > + Close + </button> + </div> + {startRate && ( + <form + onSubmit={handleSubmit} + className="flex flex-col items-center gap-3 w-full" + > + <input + type="number" + min={1} + max={100} + required + name="rating" + placeholder="rate from 1-100" + className="appearance-none w-full text-white placeholder-zinc-400 bg-white/10 py-1 px-2 rounded text-sm" + /> + <input + type="text" + name="notes" + placeholder="notes" + className="appearance-none w-full text-white placeholder-zinc-400 bg-white/10 py-1 px-2 rounded text-sm" + /> + <div className="flex gap-2 w-full"> + <button + type="submit" + className="w-full py-1 bg-action/10 hover:bg-action/20 rounded text-action" + > + Submit + </button> + <button + type="button" + onClick={handleClose} + className="w-full py-1 rounded hover:bg-white/20" + > + Cancel + </button> + </div> + </form> + )} + </div> + </> + ); +} diff --git a/components/shared/changelogs.tsx b/components/shared/changelogs.tsx index a7b0436..208b1ff 100644 --- a/components/shared/changelogs.tsx +++ b/components/shared/changelogs.tsx @@ -3,34 +3,46 @@ import Link from "next/link"; import { Fragment, useEffect, useRef, useState } from "react"; const web = { - version: "v4.3.1", + version: "v4.4.0", }; const logs = [ { version: "v4.3.1", - pre: true, + pre: false, notes: null, highlights: true, changes: [ - "Fix: Auto Next Episode forcing to play sub even if dub is selected", - "Fix: Episode metadata not showing after switching to dub", - "Fix: Profile picture weirdly cropped", - "Fix: Weird padding on the navbar in profile page", - ], - }, - { - version: "v4.3.0", - pre: true, - notes: null, - highlights: false, - changes: [ - "Added changelogs section", - "Added recommendations based on user lists", - "New Player!", - "And other minor bug fixes!", + "Added rate modal when user finished watching the whole series", + "Fix: only half of the episodes has episodes thumbnail", + "Fix: pressing back button in anime info page redirects user to the wrong page", + "Progressively migrate codebase to typescript", ], }, + // { + // version: "v4.3.1", + // pre: true, + // notes: null, + // highlights: false, + // changes: [ + // "Fix: Auto Next Episode forcing to play sub even if dub is selected", + // "Fix: Episode metadata not showing after switching to dub", + // "Fix: Profile picture weirdly cropped", + // "Fix: Weird padding on the navbar in profile page", + // ], + // }, + // { + // version: "v4.3.0", + // pre: true, + // notes: null, + // highlights: false, + // changes: [ + // "Added changelogs section", + // "Added recommendations based on user lists", + // "New Player!", + // "And other minor bug fixes!", + // ], + // }, ]; export default function ChangeLogs() { @@ -146,11 +158,11 @@ export default function ChangeLogs() { Hi! Welcome to the new changelogs section. Here you can see a lists of the latest changes and updates to the site. </p> - <p className="inline-block text-sm italic my-2 text-gray-400"> + {/* <p className="inline-block text-sm italic my-2 text-gray-400"> *This update is still in it's pre-release state, please expect to see some bugs. If you find any, please report them. - </p> + </p> */} </div> {logs.map((x) => ( @@ -166,32 +178,6 @@ export default function ChangeLogs() { </ChangelogsVersions> ))} - {/* <div className="my-2 flex items-center justify-evenly"> - <div className="w-full h-[1px] bg-gradient-to-r from-white/5 to-white/40" /> - <p className="relative flex flex-1 whitespace-nowrap font-bold mx-2 font-inter"> - v4.3.0 - <span className="flex text-xs font-light font-roboto ml-1 italic"> - pre - </span> - </p> - <div className="w-full h-[1px] bg-gradient-to-l from-white/5 to-white/40" /> - </div> - - <div className="flex flex-col gap-2 text-sm text-gray-200"> - <div> - <p className="inline-block italic mb-2 text-gray-400"> - *This update is still in it's pre-release state, please - expect to see some bugs. If you find any, please report - them. - </p> - - <p>- Added changelogs section</p> - <p>- Added recommendations based on user lists</p> - <p>- New Player!</p> - <p>- And other minor bug fixes!</p> - </div> - </div> */} - <div className="mt-2 text-gray-400 text-sm"> <p> see more changelogs{" "} diff --git a/components/watch/new-player/components/layouts/video-layout.tsx b/components/watch/new-player/components/layouts/video-layout.tsx index fa1f6c3..93e4629 100644 --- a/components/watch/new-player/components/layouts/video-layout.tsx +++ b/components/watch/new-player/components/layouts/video-layout.tsx @@ -17,13 +17,14 @@ import { Title } from "../title"; import { ChapterTitleComponent } from "../chapter-title"; import { useWatchProvider } from "@/lib/context/watchPageProvider"; import { Navigation } from "../../player"; -import BufferingIndicator from "../bufferingIndicator"; import { useEffect, useState } from "react"; +import RateModal from "@/components/shared/RateModal"; export interface VideoLayoutProps { thumbnails?: string; navigation?: Navigation; host?: boolean; + session?: any; } function isMobileDevice() { @@ -40,22 +41,40 @@ export function VideoLayout({ thumbnails, navigation, host = true, + session, }: VideoLayoutProps) { const [isMobile, setIsMobile] = useState(false); - const { track } = useWatchProvider(); + const { track, setRatingModalState, ratingModalState } = useWatchProvider(); const isFullscreen = useMediaState("fullscreen"); useEffect(() => { setIsMobile(isMobileDevice()); }, []); + useEffect(() => { + setRatingModalState((prev: any) => { + return { + ...prev, + isFullscreen: isFullscreen, + }; + }); + }, [isFullscreen]); + return ( <> <Gestures host={host} /> <Captions className={`${captionStyles.captions} media-preview:opacity-0 media-controls:bottom-[85px] media-captions:opacity-100 absolute inset-0 bottom-2 z-10 select-none break-words opacity-0 transition-[opacity,bottom] duration-300`} /> + {ratingModalState.isFullscreen && ( + <RateModal + toggle={ratingModalState.isOpen} + setToggle={setRatingModalState} + position="top" + session={session} + /> + )} <Controls.Root className={`${styles.controls} media-paused:bg-black/10 duration-200 media-controls:opacity-100 absolute inset-0 z-10 flex h-full w-full flex-col bg-gradient-to-t from-black/30 via-transparent to-black/30 opacity-0 transition-opacity`} > diff --git a/components/watch/new-player/player.tsx b/components/watch/new-player/player.tsx index b98ff79..f2d11f7 100644 --- a/components/watch/new-player/player.tsx +++ b/components/watch/new-player/player.tsx @@ -12,7 +12,6 @@ import { type MediaPlayerInstance, Track, MediaTimeUpdateEventDetail, - MediaTimeUpdateEvent, } from "@vidstack/react"; import { VideoLayout } from "./components/layouts/video-layout"; import { useWatchProvider } from "@/lib/context/watchPageProvider"; @@ -98,6 +97,7 @@ export default function VidStack({ playerState, dataMedia, autoNext, + setRatingModalState, } = useWatchProvider(); const { qualities, duration } = useMediaStore(player); @@ -379,7 +379,20 @@ export default function VidStack({ mark = 1; setMarked(1); console.log("marking progress"); - markProgress(dataMedia.id, navigation.playing.number); + // @ts-ignore Fix when convert useAnilist to typescript + markProgress({ + mediaId: dataMedia.id, + progress: navigation.playing.number, + }); + + if (dataMedia.episodes === +navigation.playing?.number) { + setRatingModalState((prev: any) => { + return { + ...prev, + isOpen: true, + }; + }); + } } } } @@ -424,7 +437,7 @@ export default function VidStack({ return ( <MediaPlayer key={id} - className={`${style.player} player`} + className={`${style.player} player relative`} title={ navigation?.playing?.title || `Episode ${navigation?.playing?.number}` || @@ -454,7 +467,11 @@ export default function VidStack({ <Track key={chapters} src={chapters} kind="chapters" default={true} /> )} </MediaProvider> - <VideoLayout thumbnails={track?.thumbnails} navigation={navigation} /> + <VideoLayout + thumbnails={track?.thumbnails} + navigation={navigation} + session={sessions} + /> </MediaPlayer> ); } diff --git a/components/watch/primary/details.tsx b/components/watch/primary/details.tsx index f20f8cf..dd739f2 100644 --- a/components/watch/primary/details.tsx +++ b/components/watch/primary/details.tsx @@ -4,6 +4,8 @@ import Skeleton from "react-loading-skeleton"; import DisqusComments from "../../disqus"; import { AniListInfoTypes } from "types/info/AnilistInfoTypes"; import { SessionTypes } from "pages/en"; +import Link from "next/link"; +import Image from "next/image"; type DetailsProps = { info: AniListInfoTypes; @@ -61,13 +63,18 @@ export default function Details({ <div className="pb-4 h-full flex"> <div className="aspect-[9/13] h-[240px]"> {info ? ( - <img - src={info.coverImage.extraLarge} - alt="Anime Cover" - width={1000} - height={1000} - className="object-cover aspect-[9/13] h-[240px] rounded-md" - /> + <Link + className="hover:scale-105 hover:shadow-lg duration-300 ease-out" + href={`/en/anime/${id}`} + > + <Image + src={info.coverImage.extraLarge} + alt="Anime Cover" + width={1000} + height={1000} + className="object-cover aspect-[9/13] h-[240px] rounded-md" + /> + </Link> ) : ( <Skeleton height={240} /> )} |