fix: move getFile to client side

This commit is contained in:
Mythie
2023-10-20 13:42:10 +11:00
parent e8f08f4083
commit 284a2870c2
6 changed files with 119 additions and 72 deletions

View File

@ -8,7 +8,7 @@ import { useRouter } from 'next/navigation';
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
import { base64 } from '@documenso/lib/universal/base64'; import { base64 } from '@documenso/lib/universal/base64';
import { putFile } from '@documenso/lib/universal/upload/put-file'; import { putFile } from '@documenso/lib/universal/upload/put-file';
import { Field, Prisma, Recipient } from '@documenso/prisma/client'; import { DocumentDataType, Field, Prisma, Recipient } from '@documenso/prisma/client';
import { Card, CardContent } from '@documenso/ui/primitives/card'; import { Card, CardContent } from '@documenso/ui/primitives/card';
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone'; import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields'; import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields';
@ -199,7 +199,14 @@ export const SinglePlayerClient = () => {
{uploadedFile ? ( {uploadedFile ? (
<Card gradient> <Card gradient>
<CardContent className="p-2"> <CardContent className="p-2">
<LazyPDFViewer document={uploadedFile.fileBase64} /> <LazyPDFViewer
documentData={{
id: '',
data: uploadedFile.fileBase64,
initialData: uploadedFile.fileBase64,
type: DocumentDataType.BYTES_64,
}}
/>
</CardContent> </CardContent>
</Card> </Card>
) : ( ) : (

View File

@ -4,7 +4,7 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { Field, Recipient, User } from '@documenso/prisma/client'; import { DocumentData, Field, Recipient, User } from '@documenso/prisma/client';
import { DocumentWithData } from '@documenso/prisma/types/document-with-data'; import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { Card, CardContent } from '@documenso/ui/primitives/card'; import { Card, CardContent } from '@documenso/ui/primitives/card';
@ -32,7 +32,7 @@ export type EditDocumentFormProps = {
document: DocumentWithData; document: DocumentWithData;
recipients: Recipient[]; recipients: Recipient[];
fields: Field[]; fields: Field[];
dataUrl: string; documentData: DocumentData;
}; };
type EditDocumentStep = 'signers' | 'fields' | 'subject'; type EditDocumentStep = 'signers' | 'fields' | 'subject';
@ -43,7 +43,7 @@ export const EditDocumentForm = ({
recipients, recipients,
fields, fields,
user: _user, user: _user,
dataUrl, documentData,
}: EditDocumentFormProps) => { }: EditDocumentFormProps) => {
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const router = useRouter();
@ -153,7 +153,7 @@ export const EditDocumentForm = ({
gradient gradient
> >
<CardContent className="p-2"> <CardContent className="p-2">
<LazyPDFViewer document={dataUrl} /> <LazyPDFViewer documentData={documentData} />
</CardContent> </CardContent>
</Card> </Card>

View File

@ -7,7 +7,6 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id'; import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getFieldsForDocument } from '@documenso/lib/server-only/field/get-fields-for-document'; import { getFieldsForDocument } from '@documenso/lib/server-only/field/get-fields-for-document';
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document'; import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
import { getFile } from '@documenso/lib/universal/upload/get-file';
import { DocumentStatus as InternalDocumentStatus } from '@documenso/prisma/client'; import { DocumentStatus as InternalDocumentStatus } from '@documenso/prisma/client';
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer'; import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
@ -43,10 +42,6 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
const { documentData } = document; const { documentData } = document;
const documentDataUrl = await getFile(documentData)
.then((buffer) => Buffer.from(buffer).toString('base64'))
.then((data) => `data:application/pdf;base64,${data}`);
const [recipients, fields] = await Promise.all([ const [recipients, fields] = await Promise.all([
await getRecipientsForDocument({ await getRecipientsForDocument({
documentId, documentId,
@ -90,13 +85,13 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
user={user} user={user}
recipients={recipients} recipients={recipients}
fields={fields} fields={fields}
dataUrl={documentDataUrl} documentData={documentData}
/> />
)} )}
{document.status === InternalDocumentStatus.COMPLETED && ( {document.status === InternalDocumentStatus.COMPLETED && (
<div className="mx-auto mt-12 max-w-2xl"> <div className="mx-auto mt-12 max-w-2xl">
<LazyPDFViewer document={documentDataUrl} /> <LazyPDFViewer documentData={documentData} />
</div> </div>
)} )}
</div> </div>

View File

@ -46,10 +46,6 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
const { documentData } = document; const { documentData } = document;
const documentDataUrl = await getFile(documentData)
.then((buffer) => Buffer.from(buffer).toString('base64'))
.then((data) => `data:application/pdf;base64,${data}`);
const { user } = await getServerComponentSession(); const { user } = await getServerComponentSession();
if ( if (
@ -86,7 +82,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
gradient gradient
> >
<CardContent className="p-2"> <CardContent className="p-2">
<LazyPDFViewer document={documentDataUrl} /> <LazyPDFViewer documentData={documentData} />
</CardContent> </CardContent>
</Card> </Card>

View File

@ -5,8 +5,9 @@ import { useState } from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog'; import * as DialogPrimitive from '@radix-ui/react-dialog';
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import { cn } from '@documenso/ui/lib/utils'; import { DocumentDataType } from '@documenso/prisma/client';
import { cn } from '../../lib/utils';
import { Dialog, DialogOverlay, DialogPortal } from '../../primitives/dialog'; import { Dialog, DialogOverlay, DialogPortal } from '../../primitives/dialog';
import { LazyPDFViewerNoLoader } from '../../primitives/lazy-pdf-viewer'; import { LazyPDFViewerNoLoader } from '../../primitives/lazy-pdf-viewer';
@ -40,7 +41,12 @@ export default function DocumentDialog({ document, ...props }: DocumentDialogPro
> >
<LazyPDFViewerNoLoader <LazyPDFViewerNoLoader
className="mx-auto w-full max-w-3xl xl:max-w-5xl" className="mx-auto w-full max-w-3xl xl:max-w-5xl"
document={`data:application/pdf;base64,${document}`} documentData={{
id: '',
data: document,
initialData: document,
type: DocumentDataType.BYTES_64,
}}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
onDocumentLoad={onDocumentLoad} onDocumentLoad={onDocumentLoad}
/> />

View File

@ -9,8 +9,12 @@ import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css'; import 'react-pdf/dist/esm/Page/TextLayer.css';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { getFile } from '@documenso/lib/universal/upload/get-file';
import { DocumentData } from '@documenso/prisma/client';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { useToast } from './use-toast';
export type LoadedPDFDocument = PDFDocumentProxy; export type LoadedPDFDocument = PDFDocumentProxy;
/** /**
@ -28,9 +32,17 @@ export type OnPDFViewerPageClick = (_event: {
pageY: number; pageY: number;
}) => void | Promise<void>; }) => void | Promise<void>;
const PDFLoader = () => (
<>
<Loader className="text-documenso h-12 w-12 animate-spin" />
<p className="text-muted-foreground mt-4">Loading document...</p>
</>
);
export type PDFViewerProps = { export type PDFViewerProps = {
className?: string; className?: string;
document: string; documentData: DocumentData;
onDocumentLoad?: (_doc: LoadedPDFDocument) => void; onDocumentLoad?: (_doc: LoadedPDFDocument) => void;
onPageClick?: OnPDFViewerPageClick; onPageClick?: OnPDFViewerPageClick;
[key: string]: unknown; [key: string]: unknown;
@ -38,13 +50,18 @@ export type PDFViewerProps = {
export const PDFViewer = ({ export const PDFViewer = ({
className, className,
document, documentData,
onDocumentLoad, onDocumentLoad,
onPageClick, onPageClick,
...props ...props
}: PDFViewerProps) => { }: PDFViewerProps) => {
const { toast } = useToast();
const $el = useRef<HTMLDivElement>(null); const $el = useRef<HTMLDivElement>(null);
const [isDocumentBytesLoading, setIsDocumentBytesLoading] = useState(false);
const [documentBytes, setDocumentBytes] = useState<Uint8Array | null>(null);
const [width, setWidth] = useState(0); const [width, setWidth] = useState(0);
const [numPages, setNumPages] = useState(0); const [numPages, setNumPages] = useState(0);
const [pdfError, setPdfError] = useState(false); const [pdfError, setPdfError] = useState(false);
@ -110,63 +127,89 @@ export const PDFViewer = ({
} }
}, []); }, []);
useEffect(() => {
const fetchDocumentBytes = async () => {
try {
setIsDocumentBytesLoading(true);
const bytes = await getFile(documentData);
setDocumentBytes(bytes);
setIsDocumentBytesLoading(false);
} catch (err) {
console.error(err);
toast({
title: 'Error',
description: 'An error occurred while loading the document.',
variant: 'destructive',
});
}
};
void fetchDocumentBytes();
}, [documentData, toast]);
return ( return (
<div ref={$el} className={cn('overflow-hidden', className)} {...props}> <div ref={$el} className={cn('overflow-hidden', className)} {...props}>
<PDFDocument {isDocumentBytesLoading ? (
file={document} <div className={cn('h-[80vh] max-h-[60rem] w-full overflow-hidden rounded')}>
className={cn('w-full overflow-hidden rounded', { <PDFLoader />
'h-[80vh] max-h-[60rem]': numPages === 0, </div>
})} ) : (
onLoadSuccess={(d) => onDocumentLoaded(d)} <PDFDocument
// Uploading a invalid document causes an error which doesn't appear to be handled by the `error` prop. file={documentBytes}
// Therefore we add some additional custom error handling. className={cn('w-full overflow-hidden rounded', {
onSourceError={() => { 'h-[80vh] max-h-[60rem]': numPages === 0,
setPdfError(true); })}
}} onLoadSuccess={(d) => onDocumentLoaded(d)}
externalLinkTarget="_blank" // Uploading a invalid document causes an error which doesn't appear to be handled by the `error` prop.
loading={ // Therefore we add some additional custom error handling.
<div className="dark:bg-background flex h-[80vh] max-h-[60rem] flex-col items-center justify-center bg-white/50"> onSourceError={() => {
{pdfError ? ( setPdfError(true);
}}
externalLinkTarget="_blank"
loading={
<div className="dark:bg-background flex h-[80vh] max-h-[60rem] flex-col items-center justify-center bg-white/50">
{pdfError ? (
<div className="text-muted-foreground text-center">
<p>Something went wrong while loading the document.</p>
<p className="mt-1 text-sm">Please try again or contact our support.</p>
</div>
) : (
<PDFLoader />
)}
</div>
}
error={
<div className="dark:bg-background flex h-[80vh] max-h-[60rem] flex-col items-center justify-center bg-white/50">
<div className="text-muted-foreground text-center"> <div className="text-muted-foreground text-center">
<p>Something went wrong while loading the document.</p> <p>Something went wrong while loading the document.</p>
<p className="mt-1 text-sm">Please try again or contact our support.</p> <p className="mt-1 text-sm">Please try again or contact our support.</p>
</div> </div>
) : (
<>
<Loader className="text-documenso h-12 w-12 animate-spin" />
<p className="text-muted-foreground mt-4">Loading document...</p>
</>
)}
</div>
}
error={
<div className="dark:bg-background flex h-[80vh] max-h-[60rem] flex-col items-center justify-center bg-white/50">
<div className="text-muted-foreground text-center">
<p>Something went wrong while loading the document.</p>
<p className="mt-1 text-sm">Please try again or contact our support.</p>
</div> </div>
</div> }
} >
> {Array(numPages)
{Array(numPages) .fill(null)
.fill(null) .map((_, i) => (
.map((_, i) => ( <div
<div key={i}
key={i} className="border-border my-8 overflow-hidden rounded border first:mt-0 last:mb-0"
className="border-border my-8 overflow-hidden rounded border first:mt-0 last:mb-0" >
> <PDFPage
<PDFPage pageNumber={i + 1}
pageNumber={i + 1} width={width}
width={width} renderAnnotationLayer={false}
renderAnnotationLayer={false} renderTextLayer={false}
renderTextLayer={false} loading={() => ''}
loading={() => ''} onClick={(e) => onDocumentPageClick(e, i + 1)}
onClick={(e) => onDocumentPageClick(e, i + 1)} />
/> </div>
</div> ))}
))} </PDFDocument>
</PDFDocument> )}
</div> </div>
); );
}; };