mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
fix: move getFile to client side
This commit is contained in:
@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user