234 lines
12 KiB
JavaScript
234 lines
12 KiB
JavaScript
import { useState } from 'react';
|
|
import { Head, Link, router } from '@inertiajs/react';
|
|
import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight, Trash2 } from 'lucide-react';
|
|
|
|
import AppLayout from '@/Layouts/AppLayout.jsx';
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { BreadcrumbItem, BreadcrumbPage, BreadcrumbSeparator } from '@/components/ui/breadcrumb';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
import { useToast } from '@/hooks/use-toast';
|
|
|
|
export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
|
|
|
const [group, setGroup] = useState('default');
|
|
const [favourites, setFavourites] = useState(auth.user.favourites);
|
|
const [ascending, setAscending] = useState(true);
|
|
|
|
const { toast } = useToast();
|
|
|
|
const favouriteOnClickHandler = (pathword) => {
|
|
axios.post(route('comics.postFavourite'), { pathword: pathword }).then(res => {
|
|
setFavourites(res.data);
|
|
toast({
|
|
title: "All set",
|
|
description: `${comic.comic.name} is now in / remove your favorite list.`,
|
|
});
|
|
});
|
|
}
|
|
|
|
const removeAllHistoriesOnClickHandler = (pathword) => {
|
|
router.visit(route('comics.destroyHistory', { pathword: pathword }), {
|
|
method: 'DELETE',
|
|
replace: false,
|
|
preserveScroll: true,
|
|
preserveState: true,
|
|
showProgress: false,
|
|
only: ['histories'],
|
|
onSuccess: () => {
|
|
toast({
|
|
title: "All set",
|
|
description: `All histories have been removed.`,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
const groupOnClickHandler = (pathword) => {
|
|
router.get(`/comic/${ comic.comic.path_word }?group=${ pathword }`, {}, {
|
|
only: ['chapters'],
|
|
preserveState: true,
|
|
onSuccess: () => {
|
|
setGroup(pathword);
|
|
setAscending(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
const ComicChapterLink = (props) => {
|
|
const isNew = Date.now() - Date.parse(props.datetime_created) < 6.048e+8;
|
|
const isRead = histories.includes(props.uuid);
|
|
|
|
return (
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button size="sm" variant="outline" asChild className={ isRead ? 'bg-gray-200 hover:bg-gray-300 dark:bg-slate-800 dark:hover:bg-slate-700' : '' }>
|
|
<Link className="relative" href={ `/comic/${ comic.comic.path_word }/${ props.uuid }` }>
|
|
{ props.name }
|
|
{ isNew && <Plus size={ 16 } className="text-xs absolute right-0 top-0" /> }
|
|
</Link>
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Updated: { props.datetime_created }</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
);
|
|
}
|
|
|
|
const toggleAscending = (e) => {
|
|
setAscending(!ascending);
|
|
}
|
|
|
|
return (
|
|
<AppLayout auth={ auth } header={
|
|
<>
|
|
<span className="hidden lg:block"><BreadcrumbSeparator /></span>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbPage>{ comic.comic.name }</BreadcrumbPage>
|
|
</BreadcrumbItem>
|
|
</>
|
|
}>
|
|
<Head>
|
|
<title>{ comic.comic.name }</title>
|
|
</Head>
|
|
<div className="p-3 pt-1">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex flex-row content-end items-center">
|
|
<Button onClick={ () => favouriteOnClickHandler(comic.comic.path_word) } size="icon" variant="ghost">
|
|
<Star fill={ favourites.includes(comic.comic.path_word) ? 'yellow': 'white' } />
|
|
</Button>
|
|
<span>{ comic.comic.name }</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="flex justify-start justify-items-stretch content-start items-start gap-5 flex-wrap">
|
|
<div className="basis-full lg:basis-2/12">
|
|
<img className="block object-fill w-full" src={ "/image/" + btoa(comic.comic.cover) }
|
|
alt={ comic.comic.name } />
|
|
</div>
|
|
<div className="basis-full lg:basis-9/12">
|
|
<table className="table-fixed w-full text-sm">
|
|
<tbody>
|
|
<tr>
|
|
<td className="text-right w-24 pr-3">Alias</td>
|
|
<td>{ comic.comic.alias }</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="text-right pr-3">Status</td>
|
|
<td>
|
|
{ comic.comic.region.display } / { comic.comic.status.display }
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="text-right pr-3">Category</td>
|
|
<td>
|
|
{ comic.comic.theme.map(t =>
|
|
<Badge key={ t.path_word } className="m-2" variant="outline">
|
|
<Link href={ route("comics.index", { tag: t.path_word }) }>{ t.name }</Link>
|
|
</Badge>
|
|
) }
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="text-right pr-3">Authors</td>
|
|
<td>
|
|
{ comic.comic.author.map(a => (
|
|
<Badge key={ a.path_word } className="m-2" variant="outline">
|
|
<Link href={ route('comics.author', [a.path_word]) }>{ a.name }</Link>
|
|
</Badge>
|
|
) ) }
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="text-right pr-3">Description</td>
|
|
<td>{ comic.comic.brief }</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="text-right pr-3">Updated At</td>
|
|
<td>
|
|
<Link href={ route('comics.read', [comic.comic.path_word, comic.comic.last_chapter.uuid])}>
|
|
{ comic.comic.datetime_updated } - { comic.comic.last_chapter.name }
|
|
</Link>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</CardContent>
|
|
<CardContent>
|
|
<Tabs defaultValue={ group } className="w-full" onValueChange={ (e) => groupOnClickHandler(e)} >
|
|
<div className="flex">
|
|
<TabsList className={ `grid w-full grid-cols-${ Object.entries(comic.groups).length } ` }>
|
|
{ Object.entries(comic.groups).map((g, i) => (
|
|
<TabsTrigger key={ g[1].path_word } value={ g[1].path_word }>
|
|
{ g[1].name }
|
|
<Badge key={ g[1].path_word } className="ml-2" variant="outline">
|
|
{ g[1].count }
|
|
</Badge>
|
|
</TabsTrigger>
|
|
) ) }
|
|
</TabsList>
|
|
<div className="flex justify-end">
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button variant="link" size="icon" onClick={ () => toggleAscending() }>
|
|
{ ascending ? <ArrowDownNarrowWide /> : <ArrowUpNarrowWide /> }
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Set order as { ascending ? 'Descending' : 'Ascending' }</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button variant="link" size="icon" onClick={ () => removeAllHistoriesOnClickHandler(comic.comic.path_word) }>
|
|
<Trash2 />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Remove all histories for this comic</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
</div>
|
|
</div>
|
|
<TabsContent value={ group }>
|
|
<div
|
|
className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 2xl:grid-cols-12 gap-1">
|
|
{ (chapters.total > chapters.limit && chapters.offset > 0) && (
|
|
<Button size="sm" variant="outline" asChild>
|
|
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) - chapters.limit }}>
|
|
<ChevronsLeft /> Prev
|
|
</Link>
|
|
</Button>
|
|
) }
|
|
{ chapters.list.sort((a, b) => ascending ? (a.index - b.index) : (b.index - a.index)).map(c => (
|
|
<ComicChapterLink key={ c.uuid } { ...c } />
|
|
) ) }
|
|
{ (chapters.total > chapters.limit && chapters.offset === 0) && (
|
|
<Button size="sm" variant="outline" asChild>
|
|
<Link href="?" only={['chapters', 'offset']} headers={{ offset: parseInt(chapters.offset) + chapters.limit }}>
|
|
Next <ChevronsRight />
|
|
</Link>
|
|
</Button>
|
|
) }
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
}
|