Skip to content

Commit

Permalink
PDF 阅读器接入
Browse files Browse the repository at this point in the history
  • Loading branch information
maxiee committed Aug 5, 2024
1 parent 2bfec78 commit 3596b4f
Show file tree
Hide file tree
Showing 9 changed files with 980 additions and 70 deletions.
617 changes: 570 additions & 47 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"@vercel/webpack-asset-relocator-loader": "1.7.3",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^6.0.0",
"electron": "31.0.1",
"eslint": "^8.0.1",
Expand Down Expand Up @@ -64,6 +65,7 @@
"pdf-parse": "^1.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-pdf": "^9.1.0",
"react-reader": "^2.0.10",
"react-router-dom": "^6.24.0",
"ts-debounce": "^4.0.0"
Expand Down
2 changes: 2 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import SettingsPage from "./pages/settings/SettingsPage";
import { configStore } from "./config/ConfigStore";
import Sha256CompletionPage from "./pages/settings/toolbox/Sha256CompletionPage";
import WeixinReadPage from "./pages/reader/weixin/WeixinReadPage";
import PDFReaderPage from "./pages/reader/pdf/PDFReaderPage";

export const bookServiceRender = createIpcProxy<IBookService>("BookService");
export const bookFileServiceRender =
Expand Down Expand Up @@ -50,6 +51,7 @@ const AppRoutes: React.FC = () => {
<Route path="/" element={<HomePage />} />
<Route path="/main_window" element={<HomePage />} />
<Route path="/read/:bookId/:fileId" element={<ReaderPage />} />
<Route path="/pdf-read/:bookId/:fileId" element={<PDFReaderPage />} />
<Route path="/weixin-read" element={<WeixinReadPage />} />
<Route path="/batch-upload" element={<BatchUploadPage />} />
<Route path="/settings" element={<SettingsPage />} />
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ const createWindow = (): void => {
// 开发环境的 CSP
csp = [
"default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: file:;",
"script-src 'self' 'unsafe-inline' 'unsafe-eval';",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: file:;",
`img-src 'self' data: ${minioEndpoint} cdn.weread.qq.com blob: file:;`,
"style-src 'self' 'unsafe-inline' blob:;",
"font-src 'self' data: file:;",
"worker-src 'self' blob:;",
`connect-src 'self' ws: ${minioEndpoint} http://localhost:* http://0.0.0.0:* file: blob:;`,
].join(" ");
} else {
Expand All @@ -76,6 +77,7 @@ const createWindow = (): void => {
`img-src 'self' data: ${minioEndpoint} file:;`,
"style-src 'self' 'unsafe-inline' blob:;",
"font-src 'self' data: file:;",
"worker-src 'self' blob:;",
`connect-src 'self' ${minioEndpoint} file: blob:;`,
].join(" ");
}
Expand Down
1 change: 0 additions & 1 deletion src/pages/home/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
import { useNavigate } from "react-router-dom";
import TabPane from "antd/es/tabs/TabPane";
import BookList from "./components/BookList";
const { ipcRenderer } = window.require("electron");

const { Header, Content } = Layout;
const { Title, Text } = Typography;
Expand Down
7 changes: 6 additions & 1 deletion src/pages/home/components/BookCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ const BookCard: React.FC<BookCardProps> = ({ book, onEdit }) => {
const handleRead = (bookFileId: Id) => {
const serializedBookId = serializeId(book._id);
const serializedFileId = serializeId(bookFileId);
navigate(`/read/${serializedBookId}/${serializedFileId}`);
const file = bookFiles.find((f) => f._id === bookFileId);
if (file && file.format.toLowerCase() === "pdf") {
navigate(`/pdf-read/${serializedBookId}/${serializedFileId}`);
} else {
navigate(`/read/${serializedBookId}/${serializedFileId}`);
}
};

const handleWeixinRead = () => {
Expand Down
129 changes: 129 additions & 0 deletions src/pages/reader/pdf/PDFReaderPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useState, useEffect, useMemo } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { Button, message } from "antd";
import { ArrowLeftOutlined } from "@ant-design/icons";
import { pdfjs, Document, Page } from "react-pdf";
import { deserializeId } from "../../../utils/DtoUtils";
import {
bookFileServiceRender,
bookServiceRender,
logServiceRender,
} from "../../../app";
import { useBookLocation } from "../epub/hooks/useBookLocation";
const { ipcRenderer } = window.require("electron");
import "react-pdf/dist/Page/AnnotationLayer.css";
import "react-pdf/dist/Page/TextLayer.css";

const PDFReaderPage: React.FC = () => {
const [numPages, setNumPages] = useState<number | null>(null);
const [pageNumber, setPageNumber] = useState(1);
const [pdfData, setPdfData] = useState<ArrayBuffer | null>(null);
const { bookId, fileId } = useParams<{ bookId: string; fileId: string }>();
const navigate = useNavigate();
const { location, setLocation, saveLocation, isLoading } =
useBookLocation(fileId);

const pdfFile = useMemo(() => {
if (pdfData) {
return { data: pdfData };
}
return null;
}, [pdfData]);

useEffect(() => {
const fetchBookFile = async () => {
logServiceRender.info("Fetching PDF file");
try {
const result = await bookFileServiceRender.getBookFileContent(
deserializeId(bookId),
deserializeId(fileId)
);
if (result.success) {
logServiceRender.debug("PDF file fetched successfully");
setPdfData(result.payload);
if (location) {
setPageNumber(parseInt(location));
}
} else {
message.error("Failed to load the PDF");
}
} catch (error) {
logServiceRender.error("Error fetching PDF file:", error);
message.error("An error occurred while loading the PDF");
}
};

const setupPdfWorker = async () => {
const workerPath = await ipcRenderer.invoke("get-pdf-worker-path");
pdfjs.GlobalWorkerOptions.workerSrc = workerPath;
};

const effect = async () => {
await setupPdfWorker();

if (bookId) {
if (!pdfData) {
await fetchBookFile();
}
// Update last read time
await bookServiceRender.updateLastReadTime(deserializeId(bookId));
}
};

effect();
}, [bookId, fileId, location, pdfData]);

const handleDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages);
};

const handlePageChange = (newPageNumber: number) => {
setPageNumber(newPageNumber);
setLocation(newPageNumber.toString());
};

const handleBackClick = async () => {
await saveLocation();
navigate("/");
};

return (
<div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<Button
icon={<ArrowLeftOutlined />}
onClick={handleBackClick}
style={{ margin: "10px" }}
>
Back to Library
</Button>
{!isLoading && pdfFile ? (
<div style={{ flex: 1, overflow: "auto" }}>
<Document file={pdfFile} onLoadSuccess={handleDocumentLoadSuccess}>
<Page pageNumber={pageNumber} />
</Document>
<div>
<p>
Page {pageNumber} of {numPages}
</p>
<Button
onClick={() => handlePageChange(pageNumber - 1)}
disabled={pageNumber <= 1}
>
Previous
</Button>
<Button
onClick={() => handlePageChange(pageNumber + 1)}
disabled={pageNumber >= numPages}
>
Next
</Button>
</div>
</div>
) : (
<div>Loading...</div>
)}
</div>
);
};

export default PDFReaderPage;
19 changes: 16 additions & 3 deletions webpack.plugins.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import type IForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import CopyPlugin from "copy-webpack-plugin";
import path from "path";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");

export const plugins = [
new ForkTsCheckerWebpackPlugin({
logger: 'webpack-infrastructure',
logger: "webpack-infrastructure",
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(
__dirname,
"node_modules/react-pdf/node_modules/pdfjs-dist/build/pdf.worker.mjs"
),
to: path.resolve(__dirname, ".webpack/pdf.worker.mjs"),
},
],
}),
];
Loading

0 comments on commit 3596b4f

Please sign in to comment.