diff --git a/packages/ui/primitives/document-password-dialog.tsx b/packages/ui/primitives/document-password-dialog.tsx new file mode 100644 index 000000000..da482bae3 --- /dev/null +++ b/packages/ui/primitives/document-password-dialog.tsx @@ -0,0 +1,51 @@ +import React from 'react'; + +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from './dialog'; + +import { Input } from './input'; +import { Button } from './button'; + +type PasswordDialogProps = { + open: boolean; + onOpenChange: (_open: boolean) => void; + setPassword: (_password: string) => void; + handleSubmit: () => void; + isError?: boolean; +} + +export const PasswordDialog = ({ open, onOpenChange, handleSubmit, isError, setPassword }: PasswordDialogProps) => { + return ( + + + + Password Required + + {isError ? ( + Incorrect password. Please try again. + ) : ( + + This document is password protected. Please enter the password to view the document. + + )} + + + + setPassword(e.target.value)} + /> + Submit + + + + ); +}; diff --git a/packages/ui/primitives/pdf-viewer.tsx b/packages/ui/primitives/pdf-viewer.tsx index 07cdaf1e2..c4184b17f 100644 --- a/packages/ui/primitives/pdf-viewer.tsx +++ b/packages/ui/primitives/pdf-viewer.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Loader } from 'lucide-react'; -import type { PDFDocumentProxy } from 'pdfjs-dist'; +import { PasswordResponses, 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'; @@ -14,6 +14,7 @@ import type { DocumentData } from '@documenso/prisma/client'; import { cn } from '../lib/utils'; import { useToast } from './use-toast'; +import { PasswordDialog } from './document-password-dialog'; export type LoadedPDFDocument = PDFDocumentProxy; @@ -60,6 +61,10 @@ export const PDFViewer = ({ const $el = useRef(null); const [isDocumentBytesLoading, setIsDocumentBytesLoading] = useState(false); + const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false); + const [password, setPassword] = useState(null); + const passwordCallbackRef = useRef<((password: string | null) => void) | null>(null); + const [isPasswordError, setIsPasswordError] = useState(false); const [documentBytes, setDocumentBytes] = useState(null); const [width, setWidth] = useState(0); @@ -77,6 +82,14 @@ export const PDFViewer = ({ setNumPages(doc.numPages); onDocumentLoad?.(doc); }; + + const handlePasswordSubmit = () => { + setIsPasswordModalOpen(false); + if (passwordCallbackRef.current) { + passwordCallbackRef.current(password); + passwordCallbackRef.current = null; + } + } const onDocumentPageClick = ( event: React.MouseEvent, @@ -169,11 +182,26 @@ export const PDFViewer = ({ ) : ( + <> { + setIsPasswordModalOpen(true); + passwordCallbackRef.current = callback; + switch (reason) { + case PasswordResponses.NEED_PASSWORD: + setIsPasswordError(false); + break; + case PasswordResponses.INCORRECT_PASSWORD: + setIsPasswordError(true); + break; + default: + break; + } + }} 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. @@ -220,7 +248,15 @@ export const PDFViewer = ({ ))} - )} + + > + )} ); };
Incorrect password. Please try again.
+ This document is password protected. Please enter the password to view the document. +