143 lines
7.5 KiB
TypeScript
143 lines
7.5 KiB
TypeScript
import {FC, useCallback, useState} from "react";
|
|
import {useDownloadChapterArchive, useFetchChapter, useGetMangaChapters} from "@/api/mangamochi";
|
|
import {Check, Database, Download, Eye, Loader2} from "lucide-react";
|
|
import {Button} from "@/components/ui/button";
|
|
import {useQueryClient} from "@tanstack/react-query";
|
|
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu";
|
|
import {useRouter} from "next/navigation";
|
|
|
|
interface MangaChapterProps {
|
|
mangaId: number;
|
|
mangaProviderId: number;
|
|
}
|
|
|
|
export const MangaChapter: FC<MangaChapterProps> = ({mangaId, mangaProviderId}) => {
|
|
const router = useRouter()
|
|
|
|
const { isPending, data, queryKey } = useGetMangaChapters(mangaProviderId);
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
const { mutate: mutateDownloadChapterArchive, isPending: isPendingDownloadChapter } = useDownloadChapterArchive({
|
|
mutation: {
|
|
onSuccess: (data, {chapterId}) => {
|
|
const url = window.URL.createObjectURL(data);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.setAttribute('download', chapterId + '.cbz');
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
link.remove();
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
}
|
|
})
|
|
|
|
const { mutate, isPending: isPendingFetchChapter } = useFetchChapter({
|
|
mutation: {
|
|
onSuccess: () => queryClient.invalidateQueries({queryKey}),
|
|
onSettled: () => setFetchingId(null)
|
|
}
|
|
});
|
|
|
|
const [fetchingId, setFetchingId] = useState<number | null>(null);
|
|
|
|
const fetchChapter = useCallback((mangaChapterId: number) => {
|
|
setFetchingId(mangaChapterId);
|
|
mutate({chapterId: mangaChapterId});
|
|
}, [mutate]);
|
|
|
|
const handleReadChapter = (chapterNumber: number) => {
|
|
router.push(`/manga/${mangaId}/chapter/${chapterNumber}`)
|
|
}
|
|
|
|
return (<div className="border-t border-border px-4 pb-4">
|
|
{
|
|
isPending ? (
|
|
<div className="flex items-center justify-center py-8">
|
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
|
<span className="ml-2 text-sm text-muted-foreground">Loading chapters...</span>
|
|
</div>
|
|
) :
|
|
data ? (
|
|
<div className="mt-4 space-y-2">
|
|
{data.map((chapter) => {
|
|
return (
|
|
<div
|
|
key={chapter.id}
|
|
className="flex items-center justify-between rounded-lg border border-border bg-background p-3"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div
|
|
className={`flex h-8 w-8 items-center justify-center rounded-full ${
|
|
chapter.isRead ? "bg-primary/20" : "bg-muted"
|
|
}`}
|
|
>
|
|
{chapter.isRead ? (
|
|
<Check className="h-4 w-4 text-primary" />
|
|
) : (
|
|
<span className="text-xs font-medium text-muted-foreground">
|
|
{/*{chapter}*/}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-foreground">{chapter.title}</p>
|
|
{chapter.downloaded && (
|
|
<p className="text-xs text-muted-foreground">In database</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{chapter.downloaded ? (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => handleReadChapter(chapter.id)}
|
|
className="gap-2"
|
|
>
|
|
<Eye className="h-4 w-4" />
|
|
Read
|
|
</Button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button size="sm" className="gap-2">
|
|
<Download className="h-4 w-4" />
|
|
Download
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuItem
|
|
disabled={isPendingDownloadChapter}
|
|
onClick={() => mutateDownloadChapterArchive({chapterId: chapter.id, params: {archiveFileType: 'CBZ'} })}
|
|
>
|
|
Download as CBZ
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
// onClick={() => handleDownloadToDevice(provider.name, chapter.number, "cbr")}
|
|
disabled
|
|
>
|
|
Download as CBR
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
) : (
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => fetchChapter(chapter.id)}
|
|
disabled={isPendingFetchChapter}
|
|
className="gap-2 cursor-pointer"
|
|
>
|
|
<Database className="h-4 w-4" />
|
|
{(isPendingFetchChapter && fetchingId == chapter.id) ? "Fetching..." : "Fetch from Provider"}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
) : null}
|
|
</div>);
|
|
}; |