fix: performance improvements (#2581)

This commit is contained in:
David Nguyen
2026-03-09 10:22:57 +11:00
committed by GitHub
parent abd031b58b
commit 6c8726b58c
30 changed files with 240 additions and 99 deletions
@@ -29,7 +29,7 @@ import { Sheet, SheetContent, SheetTrigger } from '@documenso/ui/primitives/shee
import { useToast } from '@documenso/ui/primitives/use-toast';
import { FieldAdvancedSettingsDrawer } from '~/components/embed/authoring/field-advanced-settings-drawer';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import type { TConfigureEmbedFormSchema } from './configure-document-view.types';
import type { TConfigureFieldsFormSchema } from './configure-fields-view.types';
@@ -549,7 +549,7 @@ export const ConfigureFieldsView = ({
<Form {...form}>
<div>
{normalizedDocumentData && (
<PDFViewer data={normalizedDocumentData} scrollParentRef="window" />
<PDFViewerLazy data={normalizedDocumentData} scrollParentRef="window" />
)}
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
@@ -41,7 +41,7 @@ import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signa
import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { injectCss } from '~/utils/css-vars';
import type { DirectTemplateLocalField } from '../general/direct-template/direct-template-signing-form';
@@ -340,7 +340,7 @@ export const EmbedDirectTemplateClientPage = ({
<div className="relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
{/* Viewer */}
<div className="flex-1">
<PDFViewer
<PDFViewerLazy
data={getDocumentDataUrlForPdfViewer({
envelopeId: envelopeItems[0]?.envelopeId,
envelopeItemId: envelopeItems[0]?.id,
@@ -30,7 +30,7 @@ import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signa
import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { injectCss } from '~/utils/css-vars';
import { DocumentSigningAttachmentsPopover } from '../general/document-signing/document-signing-attachments-popover';
@@ -288,7 +288,7 @@ export const EmbedSignDocumentV1ClientPage = ({
<div className="embed--DocumentContainer relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
{/* Viewer */}
<div className="embed--DocumentViewer flex-1">
<PDFViewer
<PDFViewerLazy
data={getDocumentDataUrlForPdfViewer({
envelopeId: envelopeItems[0]?.envelopeId,
envelopeItemId: envelopeItems[0]?.id,
@@ -27,7 +27,7 @@ import { Label } from '@documenso/ui/primitives/label';
import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { useRequiredDocumentSigningContext } from '../../general/document-signing/document-signing-provider';
import { DocumentSigningRejectDialog } from '../../general/document-signing/document-signing-reject-dialog';
@@ -181,8 +181,8 @@ export const MultiSignDocumentSigningView = ({
};
return (
<div className="min-h-screen overflow-hidden bg-background">
<div id="document-field-portal-root" className="relative h-full w-full overflow-y-auto p-8">
<div className="min-h-screen bg-background">
<div className="relative h-full w-full p-8">
{match({ isLoading, document })
.with({ isLoading: true }, () => (
<div className="flex min-h-[400px] w-full items-center justify-center">
@@ -229,7 +229,7 @@ export const MultiSignDocumentSigningView = ({
'md:mx-auto md:max-w-2xl': document.status === DocumentStatus.COMPLETED,
})}
>
<PDFViewer
<PDFViewerLazy
data={getDocumentDataUrlForPdfViewer({
envelopeId: document.envelopeId,
envelopeItemId: document.envelopeItems[0]?.id,
@@ -369,35 +369,39 @@ export const MultiSignDocumentSigningView = ({
</div>
</div>
)}
{hasDocumentLoaded && showPendingFieldTooltip && pendingFields.length > 0 && (
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${pendingFields[0].page}"]`}
>
<FieldToolTip
key={pendingFields[0].id}
field={pendingFields[0]}
color="warning"
>
<Trans>Click to insert field</Trans>
</FieldToolTip>
</ElementVisible>
)}
{/* Fields */}
{hasDocumentLoaded && (
<EmbedDocumentFields
fields={pendingFields}
metadata={document.documentMeta}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
)}
{/* Completed fields */}
{document.status !== DocumentStatus.COMPLETED && (
<DocumentReadOnlyFields
documentMeta={document.documentMeta ?? undefined}
fields={completedFields}
/>
)}
</div>
{hasDocumentLoaded && showPendingFieldTooltip && pendingFields.length > 0 && (
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${pendingFields[0].page}"]`}
>
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
<Trans>Click to insert field</Trans>
</FieldToolTip>
</ElementVisible>
)}
{/* Fields */}
{hasDocumentLoaded && (
<EmbedDocumentFields
fields={pendingFields}
metadata={document.documentMeta}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
)}
{/* Completed fields */}
{document.status !== DocumentStatus.COMPLETED && (
<DocumentReadOnlyFields
documentMeta={document.documentMeta ?? undefined}
fields={completedFields}
/>
)}
</>
))
.otherwise(() => null)}
@@ -19,7 +19,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
import { useRequiredDocumentSigningAuthContext } from '~/components/general/document-signing/document-signing-auth-provider';
import { useRequiredDocumentSigningContext } from '~/components/general/document-signing/document-signing-provider';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import {
DirectTemplateConfigureForm,
@@ -152,7 +152,7 @@ export const DirectTemplatePageView = ({
gradient
>
<CardContent className="p-2">
<PDFViewer
<PDFViewerLazy
key={template.id}
data={getDocumentDataUrlForPdfViewer({
envelopeId: template.envelopeId,
@@ -46,7 +46,7 @@ import { DocumentSigningRadioField } from '~/components/general/document-signing
import { DocumentSigningRejectDialog } from '~/components/general/document-signing/document-signing-reject-dialog';
import { DocumentSigningSignatureField } from '~/components/general/document-signing/document-signing-signature-field';
import { DocumentSigningTextField } from '~/components/general/document-signing/document-signing-text-field';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog';
@@ -273,7 +273,7 @@ export const DocumentSigningPageViewV1 = ({
<div className="flex-1">
<Card className="rounded-xl before:rounded-xl" gradient>
<CardContent className="p-2">
<PDFViewer
<PDFViewerLazy
key={document.envelopeItems[0]?.id}
data={getDocumentDataUrlForPdfViewer({
envelopeId: document.envelopeId,
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { Trans } from '@lingui/react/macro';
import { type DocumentData, DocumentStatus, type EnvelopeItem, EnvelopeType } from '@prisma/client';
@@ -25,7 +25,7 @@ import {
import { EnvelopeDownloadDialog } from '~/components/dialogs/envelope-download-dialog';
import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-viewer';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { EnvelopeRendererFileSelector } from '../envelope-editor/envelope-file-selector';
import { EnvelopeGenericPageRenderer } from '../envelope-editor/envelope-generic-page-renderer';
@@ -150,7 +150,7 @@ export const DocumentCertificateQRView = ({
</div>
<div className="mt-12 w-full">
<PDFViewer
<PDFViewerLazy
key={envelopeItems[0]?.id}
data={getDocumentDataUrlForPdfViewer({
envelopeId: envelopeItems[0]?.envelopeId,
@@ -184,8 +184,6 @@ const DocumentCertificateQrV2 = ({
}: DocumentCertificateQrV2Props) => {
const { envelopeItems } = useCurrentEnvelopeRender();
const scrollableContainerRef = useRef<HTMLDivElement>(null);
return (
<div className="flex min-h-screen flex-col items-start">
<div className="flex w-full flex-col justify-between gap-4 md:flex-row md:items-end">
@@ -216,12 +214,12 @@ const DocumentCertificateQrV2 = ({
/>
</div>
<div className="mt-12 max-h-[80vh] w-full overflow-y-auto" ref={scrollableContainerRef}>
<div className="mt-12 w-full">
<EnvelopeRendererFileSelector className="mb-4 p-0" fields={[]} secondaryOverride={''} />
<EnvelopePdfViewer
scrollParentRef="window"
customPageRenderer={EnvelopeGenericPageRenderer}
scrollParentRef={scrollableContainerRef}
errorMessage={PDF_VIEWER_ERROR_MESSAGES.preview}
/>
</div>
@@ -31,7 +31,7 @@ import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/ty
import { Stepper } from '@documenso/ui/primitives/stepper';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { useCurrentTeam } from '~/providers/team';
export type DocumentEditFormProps = {
@@ -441,7 +441,7 @@ export const DocumentEditForm = ({
gradient
>
<CardContent className="p-2">
<PDFViewer
<PDFViewerLazy
key={document.envelopeItems[0]?.id}
data={getDocumentDataUrlForPdfViewer({
envelopeId: document.envelopeId,
@@ -152,9 +152,12 @@ export const EnvelopeEditorFieldsPage = () => {
return (
<div className="relative flex h-full">
<div className="flex h-full w-full flex-col overflow-y-auto" ref={scrollableContainerRef}>
<div
className="flex h-full w-full flex-col overflow-y-auto px-2"
ref={scrollableContainerRef}
>
{/* Horizontal envelope item selector */}
<EnvelopeRendererFileSelector fields={editorFields.localFields} />
<EnvelopeRendererFileSelector className="px-0" fields={editorFields.localFields} />
{/* Document View */}
<div className="mt-4 flex h-full flex-col items-center justify-center">
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { faker } from '@faker-js/faker/locale/en';
import type { Faker } from '@faker-js/faker';
import { Trans } from '@lingui/react/macro';
import { FieldType, SigningStatus } from '@prisma/client';
import { FileTextIcon } from 'lucide-react';
@@ -26,9 +26,8 @@ import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-
import { EnvelopeRendererFileSelector } from './envelope-file-selector';
// Todo: Envelopes - Dynamically import faker
export const EnvelopeEditorPreviewPage = () => {
const { envelope, editorFields } = useCurrentEnvelopeEditor();
const { envelope, editorFields, editorConfig } = useCurrentEnvelopeEditor();
const { currentEnvelopeItem, fields } = useCurrentEnvelopeRender();
@@ -38,7 +37,20 @@ export const EnvelopeEditorPreviewPage = () => {
'recipient',
);
const [fakerInstance, setFakerInstance] = useState<Faker | null>(null);
useEffect(() => {
void import('@faker-js/faker/locale/en').then((mod) => {
setFakerInstance(mod.faker);
});
}, []);
const fieldsWithPlaceholders = useMemo(() => {
if (!fakerInstance) {
return [];
}
const faker = fakerInstance;
return fields.map((field) => {
const fieldMeta = ZFieldAndMetaSchema.parse(field);
@@ -189,7 +201,7 @@ export const EnvelopeEditorPreviewPage = () => {
.exhaustive(),
};
});
}, [fields, envelope, envelope.recipients, envelope.documentMeta]);
}, [fields, envelope, envelope.recipients, envelope.documentMeta, fakerInstance]);
/**
* Set the selected recipient to the first recipient in the envelope.
@@ -210,26 +222,30 @@ export const EnvelopeEditorPreviewPage = () => {
...recipient,
signingStatus: SigningStatus.SIGNED,
}))}
presignToken={editorConfig?.embedded?.presignToken}
overrideSettings={{
mode: 'export',
}}
>
<div className="relative flex h-full">
<div className="flex w-full flex-col overflow-y-auto" ref={scrollableContainerRef}>
<div
className="flex h-full w-full flex-col overflow-y-auto px-2"
ref={scrollableContainerRef}
>
{/* Horizontal envelope item selector */}
<EnvelopeRendererFileSelector fields={editorFields.localFields} />
<EnvelopeRendererFileSelector className="px-0" fields={editorFields.localFields} />
<Alert variant="warning" className="mx-auto max-w-[800px]">
<AlertTitle>
<Trans>Preview Mode</Trans>
</AlertTitle>
<AlertDescription>
<Trans>Preview what the signed document will look like with placeholder data</Trans>
</AlertDescription>
</Alert>
{/* Document View */}
<div className="mt-4 flex h-full flex-col items-center justify-center">
<Alert variant="warning" className="mb-4 max-w-[800px]">
<AlertTitle>
<Trans>Preview Mode</Trans>
</AlertTitle>
<AlertDescription>
<Trans>Preview what the signed document will look like with placeholder data</Trans>
</AlertDescription>
</Alert>
{currentEnvelopeItem !== null ? (
<EnvelopePdfViewer
customPageRenderer={EnvelopeGenericPageRenderer}
@@ -62,7 +62,12 @@ export const EnvelopeRendererFileSelector = ({
const { envelopeItems, currentEnvelopeItem, setCurrentEnvelopeItem } = useCurrentEnvelopeRender();
return (
<div className={cn('flex h-fit flex-shrink-0 space-x-2 overflow-x-auto p-4', className)}>
<div
className={cn(
'scrollbar-hidden flex h-fit flex-shrink-0 space-x-2 overflow-x-auto p-4',
className,
)}
>
{envelopeItems.map((doc, i) => (
<EnvelopeItemSelector
key={doc.id}
@@ -8,7 +8,8 @@ import { PDF_VIEWER_ERROR_MESSAGES } from '@documenso/lib/constants/pdf-viewer-i
import { cn } from '@documenso/ui/lib/utils';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { PDFViewer, type PDFViewerProps } from './pdf-viewer';
import type { PDFViewerProps } from './pdf-viewer';
import PDFViewerLazy from './pdf-viewer-lazy';
export type EnvelopePdfViewerProps = {
/**
@@ -52,7 +53,8 @@ export const EnvelopePdfViewer = ({
}
return (
<PDFViewer
<PDFViewerLazy
key={`${currentEnvelopeItem.envelopeId}-${currentEnvelopeItem.id}`}
{...props}
className={cn('h-full w-full max-w-[800px]', className)}
data={currentEnvelopeItem.data}
@@ -0,0 +1,34 @@
/**
* We need to double wrap the PDFViewer to avoid the following errors:
*
* 1. Lazy prevents the pdfjs worker from being bundled
* 2. onMount guard prevents "Worker not defined"
*/
import { Suspense, lazy, useEffect, useState } from 'react';
import { cn } from '@documenso/ui/lib/utils';
import type { PDFViewerProps } from './pdf-viewer';
import { PdfViewerLoadingState } from './pdf-viewer-states';
const PDFViewer = lazy(async () => import('./pdf-viewer'));
export default function PDFViewerLazy(props: PDFViewerProps) {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
const fallback = (
<div className={cn('h-full w-full', props.className)}>
<PdfViewerLoadingState />
</div>
);
if (!isClient) {
return fallback;
}
return <Suspense fallback={fallback}>{PDFViewer && <PDFViewer {...props} />}</Suspense>;
}
@@ -28,7 +28,14 @@ export const PdfViewerPageImage = ({ imageLoadingState, imageProps }: PdfViewerP
)}
{/* The PDF image. */}
{imageProps.src && <img {...imageProps} className={cn(imageProps.className, '')} alt="" />}
{imageProps.src && (
<img
{...imageProps}
className={cn(imageProps.className, 'select-none')}
draggable={false}
alt=""
/>
)}
</>
);
};
@@ -62,14 +62,14 @@ export type PDFViewerProps = {
customPageRenderer?: React.FunctionComponent<{ pageData: PageRenderData }>;
} & React.HTMLAttributes<HTMLDivElement>;
export const PDFViewer = ({
export default function PDFViewer({
className,
data,
scrollParentRef,
onDocumentLoad,
customPageRenderer,
...props
}: PDFViewerProps) => {
}: PDFViewerProps) {
const { t } = useLingui();
const { toast } = useToast();
@@ -77,7 +77,7 @@ export const PDFViewer = ({
const [loadingState, setLoadingState] = useState<LoadingState>('loading');
const [pdf, setPdf] = useState<pdfjsLib.PDFDocumentProxy | null>(null);
const pdfRef = useRef<pdfjsLib.PDFDocumentProxy | null>(null);
const [pages, setPages] = useState<PageMeta[]>([]);
@@ -86,11 +86,17 @@ export const PDFViewer = ({
return;
}
let isCancelled = false;
const fetchMetadata = async () => {
try {
setLoadingState('loading');
setPages([]);
if (isCancelled) {
return;
}
let result: Uint8Array | null = typeof data === 'string' ? null : new Uint8Array(data);
if (typeof data === 'string') {
@@ -103,13 +109,24 @@ export const PDFViewer = ({
result = new Uint8Array(await response.arrayBuffer());
}
const loadedPdf = await pdfjsLib.getDocument({ data: result! }).promise;
if (pdf) {
await pdf.destroy();
if (isCancelled) {
return;
}
setPdf(loadedPdf);
const loadedPdf = await pdfjsLib.getDocument({ data: result! }).promise;
if (isCancelled) {
await loadedPdf.destroy();
return;
}
// Destroy previous PDF if it exists
if (pdfRef.current) {
await pdfRef.current.destroy();
}
// eslint-disable-next-line require-atomic-updates
pdfRef.current = loadedPdf;
// Fetch the pages
const pages = await pMap(
@@ -125,10 +142,18 @@ export const PDFViewer = ({
},
);
if (isCancelled) {
return;
}
setPages(pages);
setLoadingState('loaded');
} catch (err) {
if (isCancelled) {
return;
}
console.error(err);
setLoadingState('error');
@@ -143,8 +168,11 @@ export const PDFViewer = ({
void fetchMetadata();
return () => {
if (pdf) {
void pdf.destroy();
isCancelled = true;
if (pdfRef.current) {
void pdfRef.current.destroy();
pdfRef.current = null;
}
};
}, [data]);
@@ -178,19 +206,19 @@ export const PDFViewer = ({
{hasError && <PdfViewerErrorState />}
{/* Loaded State */}
{loadingState === 'loaded' && pages.length > 0 && pdf && (
{loadingState === 'loaded' && pages.length > 0 && pdfRef.current && (
<VirtualizedPageList
scrollParentRef={scrollParentRef}
constraintRef={$el}
numPages={pages.length}
pages={pages}
pdf={pdf}
pdf={pdfRef.current}
customPageRenderer={customPageRenderer}
/>
)}
</div>
);
};
}
type VirtualizedPageListProps = {
scrollParentRef: ScrollTarget;
@@ -28,7 +28,7 @@ import { AddTemplateSettingsFormPartial } from '@documenso/ui/primitives/templat
import type { TAddTemplateSettingsFormSchema } from '@documenso/ui/primitives/template-flow/add-template-settings.types';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { useCurrentTeam } from '~/providers/team';
export type TemplateEditFormProps = {
@@ -313,7 +313,7 @@ export const TemplateEditForm = ({
gradient
>
<CardContent className="p-2">
<PDFViewer
<PDFViewerLazy
key={template.envelopeItems[0]?.id}
data={getDocumentDataUrlForPdfViewer({
envelopeId: template.envelopeId,
@@ -37,7 +37,7 @@ import { EnvelopeRendererFileSelector } from '~/components/general/envelope-edit
import { EnvelopeGenericPageRenderer } from '~/components/general/envelope-editor/envelope-generic-page-renderer';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-viewer';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { StackAvatarsWithTooltip } from '~/components/general/stack-avatars-with-tooltip';
import { useCurrentTeam } from '~/providers/team';
@@ -199,7 +199,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
/>
)}
<PDFViewer
<PDFViewerLazy
data={getDocumentDataUrlForPdfViewer({
envelopeId: envelope.id,
envelopeItemId: envelope.envelopeItems[0]?.id,
@@ -3,6 +3,7 @@ import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import { Link, Outlet, isRouteErrorResponse, redirect } from 'react-router';
import type { ShouldRevalidateFunctionArgs } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@@ -14,6 +15,10 @@ import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import type { Route } from './+types/settings._layout';
export const shouldRevalidate = ({ currentParams, nextParams }: ShouldRevalidateFunctionArgs) => {
return currentParams.id !== nextParams.id;
};
/**
* This file is very similar for templates as well. Any changes here should also be adjusted there as well.
*
@@ -24,7 +24,7 @@ import { EnvelopeRendererFileSelector } from '~/components/general/envelope-edit
import { EnvelopeGenericPageRenderer } from '~/components/general/envelope-editor/envelope-generic-page-renderer';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-viewer';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';
import { TemplateDirectLinkBadge } from '~/components/general/template/template-direct-link-badge';
import { TemplatePageViewDocumentsTable } from '~/components/general/template/template-page-view-documents-table';
import { TemplatePageViewInformation } from '~/components/general/template/template-page-view-information';
@@ -210,7 +210,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
documentMeta={mockedDocumentMeta}
/>
<PDFViewer
<PDFViewerLazy
data={getDocumentDataUrlForPdfViewer({
envelopeId: envelope.id,
envelopeItemId: envelope.envelopeItems[0]?.id,
@@ -2,7 +2,13 @@ import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import { Link, Outlet, isRouteErrorResponse, redirect } from 'react-router';
import {
Link,
Outlet,
type ShouldRevalidateFunctionArgs,
isRouteErrorResponse,
redirect,
} from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@@ -14,6 +20,10 @@ import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import type { Route } from './+types/settings._layout';
export const shouldRevalidate = ({ currentParams, nextParams }: ShouldRevalidateFunctionArgs) => {
return currentParams.id !== nextParams.id;
};
/**
* This file is very similar for documents as well. Any changes here should also be adjusted there as well.
*
@@ -20,6 +20,8 @@ import { injectCss } from '~/utils/css-vars';
import type { Route } from './+types/_layout';
export const shouldRevalidate = () => false;
export const loader = async ({ request }: Route.LoaderArgs) => {
const url = new URL(request.url);
@@ -38,6 +38,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/envelope.create._index';
export const shouldRevalidate = () => false;
export const loader = async ({ request }: Route.LoaderArgs) => {
const url = new URL(request.url);
@@ -3,7 +3,7 @@ import { useLayoutEffect, useMemo, useState } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { CheckCircle2Icon } from 'lucide-react';
import { redirect } from 'react-router';
import { type ShouldRevalidateFunctionArgs, redirect } from 'react-router';
import { EnvelopeEditorProvider } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import type { SupportedLanguageCodes } from '@documenso/lib/constants/i18n';
@@ -30,6 +30,10 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/envelope.edit.$id';
export const shouldRevalidate = ({ currentParams, nextParams }: ShouldRevalidateFunctionArgs) => {
return currentParams.id !== nextParams.id;
};
export const loader = async ({ request, params }: Route.LoaderArgs) => {
const url = new URL(request.url);
@@ -7,6 +7,7 @@ import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import type { DocumentDataVersion } from '@documenso/lib/types/document';
import { sha256 } from '@documenso/lib/universal/crypto';
import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server';
import { prisma } from '@documenso/prisma';
@@ -122,6 +123,12 @@ export const handleEnvelopeItemPdfRequest = async ({
const documentDataToUse =
version === 'current' ? envelopeItem.documentData.data : envelopeItem.documentData.initialData;
const etag = Buffer.from(sha256(documentDataToUse)).toString('hex');
if (c.req.header('If-None-Match') === etag) {
return c.status(304);
}
const file = await getFileServerSide({
type: envelopeItem.documentData.type,
data: documentDataToUse,
@@ -137,6 +144,7 @@ export const handleEnvelopeItemPdfRequest = async ({
// Note: Only set these headers on success.
c.header('Content-Type', 'application/pdf');
c.header('ETag', etag);
c.header('Cache-Control', `${cacheStrategy}, max-age=31536000, immutable`);
return c.body(file);
+1
View File
@@ -58,6 +58,7 @@ export default defineConfig({
'playwright',
'playwright-core',
'@playwright/browser-chromium',
'pdfjs-dist',
],
},
optimizeDeps: {
@@ -197,7 +197,7 @@ test.describe('Template Field Prefill API v1', () => {
// 7. Navigate to the template
await page.goto(
`${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
);
// 8. Create a document from the template with prefilled fields
@@ -488,7 +488,7 @@ test.describe('Template Field Prefill API v1', () => {
// 7. Navigate to the template
await page.goto(
`${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
);
// 8. Create a document from the template without prefilled fields
@@ -197,7 +197,7 @@ test.describe('Template Field Prefill API v2', () => {
// 7. Navigate to the template
await page.goto(
`${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
);
// 8. Create a document from the template with prefilled fields using v2 API
@@ -485,7 +485,7 @@ test.describe('Template Field Prefill API v2', () => {
// 7. Navigate to the template
await page.goto(
`${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
);
// 8. Create a document from the template without prefilled fields using v2 API
@@ -124,7 +124,10 @@ export function EnvelopeFieldToolTip({
<Tooltip delayDuration={0} open={!field.inserted || !field.fieldMeta}>
<TooltipTrigger className="absolute inset-0 w-full"></TooltipTrigger>
<TooltipContent className={tooltipVariants({ color, className })} sideOffset={2}>
<TooltipContent
className={tooltipVariants({ color, className: cn(className, 'z-40') })}
sideOffset={2}
>
{children}
<TooltipArrow />
</TooltipContent>
@@ -65,7 +65,7 @@ export function FieldToolTip({ children, color, className = '', field }: FieldTo
<TooltipTrigger className="absolute inset-0 w-full"></TooltipTrigger>
<TooltipContent
className={tooltipVariants({ color, className })}
className={tooltipVariants({ color, className: cn(className, 'z-40') })}
sideOffset={2}
onClick={onTooltipContentClick}
>
+9
View File
@@ -234,6 +234,15 @@
scrollbar-gutter: stable;
}
.scrollbar-hidden {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hidden::-webkit-scrollbar {
display: none;
}
/* .custom-scrollbar::-webkit-scrollbar-track {
border-radius: 10px;
}