From 4774324e07962c784057b135a2f58a233f391551 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 2 Dec 2025 11:29:48 +1100 Subject: [PATCH] fix: prevent client side distribution when missing signatures (#2260) --- .../dialogs/envelope-distribute-dialog.tsx | 65 +++++++++++++------ .../envelope-editor-header.tsx | 17 +---- .../envelope-editor/envelope-editor.tsx | 20 +++--- .../providers/envelope-editor-provider.tsx | 19 +++++- .../envelope-router/set-envelope-fields.ts | 2 +- .../set-envelope-fields.types.ts | 10 ++- 6 files changed, 78 insertions(+), 55 deletions(-) diff --git a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx index 92529bea2..98ffd07e3 100644 --- a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx +++ b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useLingui } from '@lingui/react/macro'; @@ -7,9 +7,7 @@ import { DocumentDistributionMethod, DocumentStatus, EnvelopeType, - type Field, FieldType, - type Recipient, RecipientRole, } from '@prisma/client'; import { AnimatePresence, motion } from 'framer-motion'; @@ -19,8 +17,8 @@ import { useNavigate } from 'react-router'; import { match } from 'ts-pattern'; import * as z from 'zod'; +import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; -import type { TEnvelope } from '@documenso/lib/types/envelope'; import { trpc, trpc as trpcReact } from '@documenso/trpc/react'; import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper'; import { cn } from '@documenso/ui/lib/utils'; @@ -52,16 +50,13 @@ import { SelectTrigger, SelectValue, } from '@documenso/ui/primitives/select'; +import { SpinnerBox } from '@documenso/ui/primitives/spinner'; import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs'; import { Textarea } from '@documenso/ui/primitives/textarea'; import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip'; import { useToast } from '@documenso/ui/primitives/use-toast'; export type EnvelopeDistributeDialogProps = { - envelope: Pick & { - recipients: Recipient[]; - fields: Pick[]; - }; onDistribute?: () => Promise; documentRootPath: string; trigger?: React.ReactNode; @@ -86,20 +81,20 @@ export const ZEnvelopeDistributeFormSchema = z.object({ export type TEnvelopeDistributeFormSchema = z.infer; export const EnvelopeDistributeDialog = ({ - envelope, trigger, documentRootPath, onDistribute, }: EnvelopeDistributeDialogProps) => { const organisation = useCurrentOrganisation(); - const recipients = envelope.recipients; + const { envelope, syncEnvelope, isAutosaving, autosaveError } = useCurrentEnvelopeEditor(); const { toast } = useToast(); const { t } = useLingui(); const navigate = useNavigate(); const [isOpen, setIsOpen] = useState(false); + const [isSyncing, setIsSyncing] = useState(false); const { mutateAsync: distributeEnvelope } = trpcReact.envelope.distribute.useMutation(); @@ -189,6 +184,29 @@ export const EnvelopeDistributeDialog = ({ } }; + const handleSync = async () => { + if (isSyncing) { + return; + } + + setIsSyncing(true); + + try { + await syncEnvelope(); + } catch (err) { + console.error(err); + } + + setIsSyncing(false); + }; + + useEffect(() => { + // Resync the whole envelope if the envelope is mid saving. + if (isOpen && (isAutosaving || autosaveError)) { + void handleSync(); + } + }, [isOpen]); + if (envelope.status !== DocumentStatus.DRAFT || envelope.type !== EnvelopeType.DOCUMENT) { return null; } @@ -208,7 +226,7 @@ export const EnvelopeDistributeDialog = ({ - {!invalidEnvelopeCode ? ( + {!invalidEnvelopeCode || isSyncing ? (
@@ -236,7 +254,16 @@ export const EnvelopeDistributeDialog = ({ })} > - {distributionMethod === DocumentDistributionMethod.EMAIL && ( + {isSyncing ? ( + + + + ) : distributionMethod === DocumentDistributionMethod.EMAIL ? ( - + @@ -347,7 +374,7 @@ export const EnvelopeDistributeDialog = ({