Skip to content

Commit

Permalink
feat: Add PDFViewer
Browse files Browse the repository at this point in the history
  • Loading branch information
NriotHrreion committed Aug 3, 2024
1 parent 6b49db9 commit 91249ff
Show file tree
Hide file tree
Showing 12 changed files with 2,601 additions and 1,876 deletions.
15 changes: 10 additions & 5 deletions app/(pages)/explorer/viewer/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"use client";

import React, { useEffect } from "react";
import { toast } from "react-toastify";

import { parseStringPath, useExplorer } from "@/hooks/useExplorer";
import { concatPath } from "@/lib/utils";
Expand All @@ -14,10 +15,9 @@ import TextViewer from "@/components/viewers/text-viewer";
import ImageViewer from "@/components/viewers/image-viewer";
import VideoViewer from "@/components/viewers/video-viewer";
import AudioViewer from "@/components/viewers/audio-viewer";
import { toast } from "react-toastify";
import { useRouter } from "next/navigation";
import PDFViewer from "@/components/viewers/pdf-viewer";

function getViewer(type: string): typeof React.Component<ViewerProps> | null {
export function getViewer(type: string): typeof React.Component<ViewerProps> | null {
switch(type) {
case "text":
case "command":
Expand All @@ -36,6 +36,8 @@ function getViewer(type: string): typeof React.Component<ViewerProps> | null {
return AudioViewer;
case "video":
return VideoViewer;
case "pdf":
return PDFViewer;
default:
return null;
}
Expand All @@ -50,7 +52,6 @@ export default function Page({ searchParams }: {
}) {
const { type, folder, file } = searchParams;
const explorer = useExplorer();
const router = useRouter();

const ViewerComponent = getViewer(type);

Expand All @@ -62,10 +63,14 @@ export default function Page({ searchParams }: {

if(!ViewerComponent) {
toast.error("暂不支持打开此类型的文件");
router.back();

return <></>;
}

if(typeof window === "undefined") {
// Fuck you ssr
return <ViewerComponent path={explorer.disk + concatPath(folder, file)} fileName={file}/>;
}

return <ViewerComponent path={(explorer.disk || storage.getItem(diskStorageKey, "C:")) + concatPath(folder, file)} fileName={file}/>;
}
2 changes: 1 addition & 1 deletion app/api/fs/disks/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os from "node:os";

import { NextRequest } from "next/server";
import diskinfo from "node-disk-info";
import * as diskinfo from "node-disk-info";

import { tokenStorageKey } from "@/lib/global";
import { validateToken } from "@/lib/token";
Expand Down
8 changes: 5 additions & 3 deletions components/explorer/explorer-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { toast } from "react-toastify";

import { useExplorer } from "@/hooks/useExplorer";
import { formatSize, getFileType, getFileTypeName } from "@/lib/utils";
import { getViewer } from "@/app/(pages)/explorer/viewer/page";

export function getFolderIcon(folderName: string, size: number = 18, color?: string): React.ReactNode {
const folderNameLowered = folderName.toLowerCase();
Expand Down Expand Up @@ -96,14 +97,15 @@ const ExplorerItem: React.FC<ExplorerItemProps> = (props) => {
setSelected(false);

if(props.type === "file") {
explorer.setCurrentViewing(props.name);
const viewerType = getFileType(extname ?? "")?.id;

if(!viewerType) {
if(!viewerType || !getViewer(viewerType)) {
toast.error("暂不支持打开此类型的文件");

return;
}

explorer.setCurrentViewing(props.name);
router.push(`/explorer/viewer?type=${viewerType}&folder=${explorer.stringifyPath()}&file=${props.name}`);

return;
Expand Down
2 changes: 0 additions & 2 deletions components/viewers/audio-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Viewer, { ViewerProps } from ".";

import PlayIcon from "@/styles/icons/play.svg";
import PauseIcon from "@/styles/icons/pause.svg";
import { emitter } from "@/lib/emitter";

interface AudioViewerProps extends ViewerProps {}

Expand Down Expand Up @@ -179,7 +178,6 @@ export default class AudioViewer extends Viewer<AudioViewerProps, AudioViewerSta
this.setState({ value: "" });

this.eventController.abort();
emitter.removeAllListeners("viewer:audio-player-time-change");
}

private initEvents() {
Expand Down
95 changes: 95 additions & 0 deletions components/viewers/pdf-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client";

import React from "react";
import { ArrowLeft, ArrowRight } from "lucide-react";
import { Button } from "@nextui-org/button";
import { pdfjs, Document, Page } from "react-pdf";

import Viewer, { ViewerProps } from ".";

import "react-pdf/dist/Page/AnnotationLayer.css";
import "react-pdf/dist/Page/TextLayer.css";

/** @see https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#legacy-pdfjs-worker */
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
`https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`,
import.meta.url,
).toString();

interface PDFViewerProps extends ViewerProps {}

interface PDFViewerState {
value: string
currentPage: number
totalPages: number
}

export default class PDFViewer extends Viewer<PDFViewerProps, PDFViewerState> {
public constructor(props: PDFViewerProps) {
super(props, "PDF查看器");

this.state = {
value: "", // doc url,
currentPage: 1,
totalPages: 0
};
}

private changePage = (delta: number) => {
if(this.state.currentPage + delta < 1 || this.state.currentPage + delta > this.state.totalPages) return;

this.setState({ currentPage: this.state.currentPage + delta });
}

public render(): React.ReactNode {
return (
<div className="w-full h-full flex flex-col items-center">
<div className="w-full h-6 mb-2 px-4 flex justify-between">
<div className="flex items-center">
<Button
variant="light"
size="sm"
isIconOnly
disableAnimation
isDisabled={this.state.currentPage === 1}
onPress={() => this.changePage(-1)}>
<ArrowLeft size={15}/>
</Button>
<Button
variant="light"
size="sm"
isIconOnly
disableAnimation
isDisabled={this.state.currentPage === this.state.totalPages}
onPress={() => this.changePage(1)}>
<ArrowRight size={15}/>
</Button>
<span className="text-default-400 text-sm ml-2">{this.props.fileName}</span>
</div>

<div className="flex items-center gap-2">
<span className="text-default-400 text-sm">{this.state.currentPage} 页 / 共 {this.state.totalPages}</span>
<span className="text-default-400 text-sm font-semibold">React PDF</span>
</div>
</div>

<div className="rounded-lg overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-default-200 hover:scrollbar-thumb-default-300 active:scrollbar-thumb-default-400 scrollbar-thumb-rounded-sm">
<Document
file={this.state.value}
onLoadSuccess={({ numPages }) => this.setState({ totalPages: numPages })}>
<Page pageNumber={this.state.currentPage}/>
</Document>
</div>
</div>
);
}

public async componentDidMount() {
this.setState({ value: URL.createObjectURL(new Blob([Buffer.from(await this.loadFile())])) });
}

public componentWillUnmount() {
URL.revokeObjectURL(this.state.value);
this.setState({ value: "" });
}
}
2 changes: 0 additions & 2 deletions components/viewers/video-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import Viewer, { ViewerProps } from ".";

import PlayIcon from "@/styles/icons/play.svg";
import PauseIcon from "@/styles/icons/pause.svg";
import { emitter } from "@/lib/emitter";

interface VideoViewerProps extends ViewerProps {}

Expand Down Expand Up @@ -148,7 +147,6 @@ export default class VideoViewer extends Viewer<VideoViewerProps, VideoViewerSta
this.setState({ value: "" });

this.eventController.abort();
emitter.removeAllListeners("viewer:audio-player-time-change");
}

private initEvents() {
Expand Down
1 change: 1 addition & 0 deletions config/fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export const fontSans = FontSans({
});

export const fontNoto = FontNoto({
subsets: ["latin"],
variable: "--font-noto",
});
26 changes: 0 additions & 26 deletions hooks/useEmitter.ts

This file was deleted.

3 changes: 0 additions & 3 deletions lib/emitter.ts

This file was deleted.

5 changes: 5 additions & 0 deletions lib/store/file-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"name": "Excel 工作表",
"extensions": ["xls", "xlsx"]
},
{
"id": "pdf",
"name": "PDF 文档",
"extensions": ["pdf"]
},
{
"id": "archive",
"name": "压缩包",
Expand Down
Loading

0 comments on commit 91249ff

Please sign in to comment.