fix: remove lazy pdf loader

This commit is contained in:
David Nguyen
2025-02-26 21:48:06 +11:00
parent 6474b4a524
commit 596d30e2e5
25 changed files with 14590 additions and 14938 deletions

View File

@ -6,7 +6,7 @@ import { X } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Dialog, DialogOverlay, DialogPortal, DialogTrigger } from '../../primitives/dialog';
import { LazyPDFViewerNoLoader } from '../../primitives/lazy-pdf-viewer';
import PDFViewer from '../../primitives/pdf-viewer';
export type DocumentDialogProps = {
trigger?: React.ReactNode;
@ -43,7 +43,7 @@ export default function DocumentDialog({ trigger, documentData, ...props }: Docu
)}
onClick={() => props.onOpenChange?.(false)}
>
<LazyPDFViewerNoLoader
<PDFViewer
className="mx-auto w-full max-w-3xl xl:max-w-5xl"
documentData={documentData}
onClick={(e) => e.stopPropagation()}

View File

@ -1,107 +0,0 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Button } from './button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './dialog';
import { Form, FormControl, FormField, FormItem, FormMessage } from './form/form';
import { Input } from './input';
const ZPasswordDialogFormSchema = z.object({
password: z.string(),
});
type TPasswordDialogFormSchema = z.infer<typeof ZPasswordDialogFormSchema>;
type PasswordDialogProps = {
open: boolean;
onOpenChange: (_open: boolean) => void;
defaultPassword?: string;
onPasswordSubmit?: (password: string) => void;
isError?: boolean;
};
export const PasswordDialog = ({
open,
onOpenChange,
defaultPassword,
onPasswordSubmit,
isError,
}: PasswordDialogProps) => {
const { _ } = useLingui();
const form = useForm<TPasswordDialogFormSchema>({
defaultValues: {
password: defaultPassword ?? '',
},
resolver: zodResolver(ZPasswordDialogFormSchema),
});
const onFormSubmit = ({ password }: TPasswordDialogFormSchema) => {
onPasswordSubmit?.(password);
};
useEffect(() => {
if (isError) {
form.setError('password', {
type: 'manual',
message: _(msg`The password you have entered is incorrect. Please try again.`),
});
}
}, [form, isError]);
return (
<Dialog open={open}>
<DialogContent className="w-full max-w-md">
<DialogHeader>
<DialogTitle>
<Trans>Password Required</Trans>
</DialogTitle>
<DialogDescription className="text-muted-foreground">
<Trans>
This document is password protected. Please enter the password to view the document.
</Trans>
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex flex-wrap items-start justify-between gap-4">
<FormField
name="password"
control={form.control}
render={({ field }) => (
<FormItem className="relative flex-1">
<FormControl>
<Input
type="password"
className="bg-background"
placeholder={_(msg`Enter password`)}
autoComplete="off"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div>
<Button>
<Trans>Submit</Trans>
</Button>
</div>
</fieldset>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@ -1,31 +0,0 @@
// Todo: (RR7) Not sure if this actually makes it client-only.
import { Suspense, lazy } from 'react';
import { Trans } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { Await } from 'react-router';
const LoadingComponent = () => (
<div className="dark:bg-background flex h-[80vh] max-h-[60rem] flex-col items-center justify-center bg-white/50">
<Loader className="text-documenso h-12 w-12 animate-spin" />
<p className="text-muted-foreground mt-4">
<Trans>Loading document...</Trans>
</p>
</div>
);
export const LazyPDFViewerImport = lazy(async () => import('./pdf-viewer'));
export const LazyPDFViewer = (props: React.ComponentProps<typeof LazyPDFViewerImport>) => (
<Suspense fallback={<LoadingComponent />}>
<Await resolve={LazyPDFViewerImport}>
<LazyPDFViewerImport {...props} />
</Await>
</Suspense>
);
export const LazyPDFViewerNoLoader = (props: React.ComponentProps<typeof LazyPDFViewer>) => (
<Suspense fallback={null}>
<LazyPDFViewerImport {...props} />
</Suspense>
);

View File

@ -5,18 +5,15 @@ import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentData } from '@prisma/client';
import { Loader } from 'lucide-react';
import { type PDFDocumentProxy, PasswordResponses } from 'pdfjs-dist';
import pdfWorker from 'pdfjs-dist/build/pdf.worker.min?url';
import { type PDFDocumentProxy } from 'pdfjs-dist';
import { Document as PDFDocument, Page as PDFPage, pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';
import { match } from 'ts-pattern';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { getFile } from '@documenso/lib/universal/upload/get-file';
import { cn } from '../lib/utils';
import { PasswordDialog } from './document-password-dialog';
import { useToast } from './use-toast';
export type LoadedPDFDocument = PDFDocumentProxy;
@ -24,7 +21,10 @@ export type LoadedPDFDocument = PDFDocumentProxy;
/**
* This imports the worker from the `pdfjs-dist` package.
*/
pdfjs.GlobalWorkerOptions.workerSrc = pdfWorker;
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.js',
import.meta.url,
).toString();
export type OnPDFViewerPageClick = (_event: {
pageNumber: number;
@ -49,8 +49,6 @@ const PDFLoader = () => (
export type PDFViewerProps = {
className?: string;
documentData: DocumentData;
password?: string | null;
onPasswordSubmit?: (password: string) => void | Promise<void>;
onDocumentLoad?: (_doc: LoadedPDFDocument) => void;
onPageClick?: OnPDFViewerPageClick;
[key: string]: unknown;
@ -59,8 +57,6 @@ export type PDFViewerProps = {
export const PDFViewer = ({
className,
documentData,
password: defaultPassword,
onPasswordSubmit,
onDocumentLoad,
onPageClick,
...props
@ -70,11 +66,7 @@ export const PDFViewer = ({
const $el = useRef<HTMLDivElement>(null);
const passwordCallbackRef = useRef<((password: string | null) => void) | null>(null);
const [isDocumentBytesLoading, setIsDocumentBytesLoading] = useState(false);
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false);
const [isPasswordError, setIsPasswordError] = useState(false);
const [documentBytes, setDocumentBytes] = useState<Uint8Array | null>(null);
const [width, setWidth] = useState(0);
@ -190,21 +182,6 @@ export const PDFViewer = ({
className={cn('w-full overflow-hidden rounded', {
'h-[80vh] max-h-[60rem]': numPages === 0,
})}
onPassword={(callback, reason) => {
// If the document already has a password, we don't need to ask for it again.
if (defaultPassword && reason !== PasswordResponses.INCORRECT_PASSWORD) {
callback(defaultPassword);
return;
}
setIsPasswordModalOpen(true);
passwordCallbackRef.current = callback;
match(reason)
.with(PasswordResponses.NEED_PASSWORD, () => setIsPasswordError(false))
.with(PasswordResponses.INCORRECT_PASSWORD, () => setIsPasswordError(true));
}}
onLoadSuccess={(d) => onDocumentLoaded(d)}
// Uploading a invalid document causes an error which doesn't appear to be handled by the `error` prop.
// Therefore we add some additional custom error handling.
@ -263,19 +240,6 @@ export const PDFViewer = ({
</div>
))}
</PDFDocument>
<PasswordDialog
open={isPasswordModalOpen}
onOpenChange={setIsPasswordModalOpen}
onPasswordSubmit={(password) => {
passwordCallbackRef.current?.(password);
setIsPasswordModalOpen(false);
void onPasswordSubmit?.(password);
}}
isError={isPasswordError}
/>
</>
)}
</div>