Skip to content

Commit

Permalink
Merge pull request #45 from rdwxth/main
Browse files Browse the repository at this point in the history
add epub reader and continue reading features;
  • Loading branch information
rdwxth authored Dec 25, 2024
2 parents 0e2a1c0 + 694f553 commit 36c6896
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 46 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.2.0-rc.7",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
Expand Down
95 changes: 95 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions src/api/backend/search/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,18 @@ export const useGetBooksQueryWithExternalDownloads = (params: SearchParams) => {
enabled: params.query !== "",
});
};

export const useGetBooksByMd5sQuery = (md5s: string[]) => {
return useQuery({
queryKey: ["search", md5s],
queryFn: async () => {
const books: BookItem[] = [];
for (const md5 of md5s) {
const response = await getBooks({ query: md5, lang: "all", limit: 1 });
books.push(response.results[0]);
}
return books;
},
enabled: md5s.length > 0,
});
};
14 changes: 10 additions & 4 deletions src/api/backend/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { ExternalDownloadLink } from "./downloads/types";

export interface BookItem {
authors: string;
book_content: string;
author: string;
book_filetype: string;
book_image: string;
book_lang: string;
book_length: string;
book_size: string;
book_source: string;
cid: string;
description: string;
external_cover_url: string;
id: number;
isbn: string;
link: string;
md5: string;
publication: string[];
other_titles: string;
publisher: string;
series: string;
title: string;
year: string;
}

export interface BookItemWithExternalDownloads extends BookItem {
Expand Down
38 changes: 30 additions & 8 deletions src/components/books/book-item.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useMemo } from "react";
import { BookItem, BookItemWithExternalDownloads } from "@/api/backend/types";
import { Card, CardContent } from "../ui/card";
import PlaceholderImage from "@/assets/placeholder.png";
Expand All @@ -9,13 +9,25 @@ import { BookmarkButton } from "./bookmark";
import { BookDownloadButton } from "./download-button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog";
import { ScrollArea } from "../ui/scroll-area";
import { Progress } from "../ui/progress";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
import { useReadingProgressStore } from "@/stores/progress";

type BookItemProps = BookItemWithExternalDownloads | BookItem;

export function BookItemCard(props: BookItemProps) {
const [isReaderOpen, setIsReaderOpen] = useState(false);
const findReadingProgress = useReadingProgressStore((state) => state.findReadingProgress);

const isEpub = Boolean(props.link?.toLowerCase().endsWith(".epub"));

const progress = useMemo(() => {
const progress = findReadingProgress(props.md5);
if (progress && progress.totalPages > 0) {
return (progress.currentPage / progress.totalPages) * 100;
}
}, [props.md5, findReadingProgress]);

return (
<Card className="shadow-md transition-shadow duration-300 hover:shadow-lg">
<CardContent className="relative flex h-full w-full items-center p-4 md:p-6">
Expand All @@ -24,7 +36,7 @@ export function BookItemCard(props: BookItemProps) {
</div>

<div className="flex w-full flex-col gap-4 pt-12 sm:pt-0 md:flex-row md:gap-6">
<div className="mx-2 flex w-full max-w-[200px] items-center justify-center md:w-1/4">
<div className="mx-2 flex w-full max-w-[200px] flex-col items-center justify-center md:w-1/4">
<AspectRatio ratio={5 / 8} className="flex items-center">
<img
src={props.book_image ?? PlaceholderImage}
Expand All @@ -36,22 +48,33 @@ export function BookItemCard(props: BookItemProps) {
onClick={() => setIsReaderOpen(true)}
/>
</AspectRatio>
{progress != null && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Progress value={progress} className="mt-2" />
</TooltipTrigger>
<TooltipContent>
<p className="text-xs dark:text-gray-400">Progress: {progress!.toFixed(2)}%</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
<div className="flex flex-1 flex-col justify-between">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<h2 className="max-w-[90%] text-2xl font-bold">{props.title}</h2>
<p className="text-md dark:text-gray-400">By {props.authors}</p>
<p className="text-md dark:text-gray-400">By {props.author}</p>
</div>
<p className="line-clamp-3 break-all text-sm dark:text-gray-400">{props.description}</p>
<p className="text-sm dark:text-gray-400">{props.book_content}</p>
<p className="text-sm dark:text-gray-400">File size: {props.book_size}</p>
<p className="text-sm dark:text-gray-400">File type: {props.book_filetype}</p>
<p className="text-sm dark:text-gray-400">MD5: {props.md5}</p>
</div>
<div className="mt-4 flex flex-wrap gap-5">
{"externalDownloads" in props && <BookDownloadButton title={props.title} extension={props.book_filetype} externalDownloads={props.externalDownloads} primaryLink={props.link} />}
{isEpub && <EpubReader title={props.title} link={props.link} open={isReaderOpen} setIsOpen={setIsReaderOpen} />}
{isEpub && <EpubReader title={props.title} md5={props.md5} link={props.link} open={isReaderOpen} setIsOpen={setIsReaderOpen} />}
</div>
</div>
</div>
Expand Down Expand Up @@ -118,11 +141,10 @@ export function BookItemDialog(props: BookItemProps) {
<DialogContent>
<DialogHeader>
<DialogTitle>{props.title}</DialogTitle>
<DialogDescription>By {props.authors}</DialogDescription>
<DialogDescription>By {props.author}</DialogDescription>
</DialogHeader>
<ScrollArea className="max-h-[80vh]">
<div className="flex flex-col gap-4">
<p>{props.book_content}</p>
<p>File size: {props.book_size}</p>
<p>File type: {props.book_filetype}</p>
<p>MD5: {props.md5}</p>
Expand All @@ -132,7 +154,7 @@ export function BookItemDialog(props: BookItemProps) {
<DialogFooter className="flex flex-row justify-between md:justify-end">
{"externalDownloads" in props && <BookDownloadButton title={props.title} extension={props.book_filetype} externalDownloads={props.externalDownloads} primaryLink={props.link} />}

{isEpub && <EpubReader title={props.title} link={props.link} open={isReaderOpen} setIsOpen={setIsReaderOpen} />}
{isEpub && <EpubReader title={props.title} md5={props.md5} link={props.link} open={isReaderOpen} setIsOpen={setIsReaderOpen} />}
</DialogFooter>
</DialogContent>
</Dialog>
Expand Down
Loading

0 comments on commit 36c6896

Please sign in to comment.