feat: add loading spinner to ComicPage component

This commit is contained in:
monoid 2025-08-15 22:01:04 +09:00
parent bd536f2ea5
commit 482892ffc1

View file

@ -8,6 +8,7 @@ import { EnterFullScreenIcon, ExitFullScreenIcon, ExitIcon } from "@radix-ui/rea
import { useEventListener } from "usehooks-ts"; import { useEventListener } from "usehooks-ts";
import type { Document } from "dbtype"; import type { Document } from "dbtype";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { Loader2 } from "lucide-react";
interface ComicPageProps { interface ComicPageProps {
params: { params: {
@ -47,7 +48,7 @@ function ComicViewer({
}, [pageDown, pageUp]); }, [pageDown, pageUp]);
useEffect(() => { useEffect(() => {
if(currentImageRef.current){ if (currentImageRef.current) {
if (curPage < 0 || curPage >= totalPage) { if (curPage < 0 || curPage >= totalPage) {
return; return;
} }
@ -83,7 +84,7 @@ function ComicViewer({
className={cn("max-w-full max-h-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 absolute", className={cn("max-w-full max-h-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 absolute",
fade ? "opacity-70 transition-opacity duration-300 ease-in-out" : "opacity-100" fade ? "opacity-70 transition-opacity duration-300 ease-in-out" : "opacity-100"
)} )}
alt="main content"/> alt="main content" />
<div className="absolute right-0 w-1/2 h-full z-10 select-none" onMouseDown={() => pageUp(1)} /> <div className="absolute right-0 w-1/2 h-full z-10 select-none" onMouseDown={() => pageUp(1)} />
{curPage + 1 < totalPage && ( {curPage + 1 < totalPage && (
<img src={`/api/doc/${doc.id}/comic/${curPage + 1}`} alt="next page" className="sr-only" /> <img src={`/api/doc/${doc.id}/comic/${curPage + 1}`} alt="next page" className="sr-only" />
@ -121,9 +122,11 @@ export default function ComicPage({
const [curPage, setCurPage] = useState(0); const [curPage, setCurPage] = useState(0);
const { isFullScreen, toggleFullScreen } = useFullScreen(); const { isFullScreen, toggleFullScreen } = useFullScreen();
if (isLoading) { if (isLoading) {
// TODO: Add a loading spinner return <div className="p-4 flex items-center justify-center gap-2 h-dvh">
return <div className="p-4"> <Loader2
Loading... className="w-6 h-6 animate-spin text-primary"
/>
<span className="text-muted-foreground"> ...</span>
</div> </div>
} }
if (error) { if (error) {
@ -143,27 +146,27 @@ export default function ComicPage({
return ( return (
<PageNavItem items={<> <PageNavItem items={<>
<NavItem to={`/doc/${params.id}`} name="Back" icon={<ExitIcon />}/> <NavItem to={`/doc/${params.id}`} name="Back" icon={<ExitIcon />} />
<NavItemButton name={isFullScreen ? "Exit Fullscreen" : "Enter Fullscreen"} icon={isFullScreen ? <ExitFullScreenIcon/> : <EnterFullScreenIcon/>} onClick={()=>{ <NavItemButton name={isFullScreen ? "Exit Fullscreen" : "Enter Fullscreen"} icon={isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />} onClick={() => {
toggleFullScreen(); toggleFullScreen();
}} /> }} />
<Popover> <Popover>
<PopoverTrigger> <PopoverTrigger>
<span className="text-sm text-ellipsis" >{curPage + 1}/{data.additional.page as number}</span> <span className="text-sm text-ellipsis" >{curPage + 1}/{data.additional.page as number}</span>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-28"> <PopoverContent className="w-28">
<Input type="number" value={curPage + 1} onChange={(e) => <Input type="number" value={curPage + 1} onChange={(e) =>
setCurPage(clip(Number.parseInt(e.target.value) - 1, setCurPage(clip(Number.parseInt(e.target.value) - 1,
0, 0,
(data.additional.page as number) - 1))} /> (data.additional.page as number) - 1))} />
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</>}> </>}>
<ComicViewer <ComicViewer
curPage={curPage} curPage={curPage}
onChangePage={setCurPage} onChangePage={setCurPage}
doc={data} doc={data}
totalPage={data.additional.page as number} /> totalPage={data.additional.page as number} />
</PageNavItem> </PageNavItem>
) )
} }