mirror of
https://github.com/documenso/documenso.git
synced 2026-08-27 00:32:29 +10:00
fix: wip
This commit is contained in:
@@ -7,7 +7,10 @@ import {
|
||||
} from '@documenso/lib/types/envelope-editor';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TSetEnvelopeFieldsResponse } from '@documenso/trpc/server/envelope-router/set-envelope-fields.types';
|
||||
import type { TSetEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/set-envelope-recipients.types';
|
||||
import type {
|
||||
TSetEnvelopeRecipientsRequest,
|
||||
TSetEnvelopeRecipientsResponse,
|
||||
} from '@documenso/trpc/server/envelope-router/set-envelope-recipients.types';
|
||||
import type { TUpdateEnvelopeRequest } from '@documenso/trpc/server/envelope-router/update-envelope.types';
|
||||
import type { TRecipientColor } from '@documenso/ui/lib/recipient-colors';
|
||||
import { getRecipientColor } from '@documenso/ui/lib/recipient-colors';
|
||||
@@ -29,6 +32,14 @@ export type EnvelopeEditorStep = 'upload' | 'addFields' | 'preview';
|
||||
|
||||
type UpdateEnvelopePayload = Pick<TUpdateEnvelopeRequest, 'data' | 'meta'>;
|
||||
|
||||
type SetRecipientsPayload = (TSetEnvelopeRecipientsRequest['recipients'][number] & {
|
||||
/**
|
||||
* Stable client-side key for the signer row, echoed to the server as
|
||||
* `clientId` so newly created recipients can adopt their server-assigned id.
|
||||
*/
|
||||
formId?: string;
|
||||
})[];
|
||||
|
||||
type EnvelopeEditorProviderValue = {
|
||||
editorConfig: EnvelopeEditorConfig;
|
||||
|
||||
@@ -47,8 +58,8 @@ type EnvelopeEditorProviderValue = {
|
||||
setLocalEnvelope: (localEnvelope: Partial<TEditorEnvelope>) => void;
|
||||
updateEnvelope: (envelopeUpdates: UpdateEnvelopePayload) => void;
|
||||
updateEnvelopeAsync: (envelopeUpdates: UpdateEnvelopePayload) => Promise<void>;
|
||||
setRecipientsDebounced: (recipients: TSetEnvelopeRecipientsRequest['recipients']) => void;
|
||||
setRecipientsAsync: (recipients: TSetEnvelopeRecipientsRequest['recipients']) => Promise<void>;
|
||||
setRecipientsDebounced: (recipients: SetRecipientsPayload) => void;
|
||||
setRecipientsAsync: (recipients: SetRecipientsPayload) => Promise<void>;
|
||||
|
||||
getRecipientColorKey: (recipientId: number) => TRecipientColor;
|
||||
|
||||
@@ -170,6 +181,41 @@ export const EnvelopeEditorProvider = ({
|
||||
const externalFlushCallbacksRef = useRef<Map<string, () => Promise<void>>>(new Map());
|
||||
const pendingMutationsRef = useRef<Set<Promise<unknown>>>(new Set());
|
||||
|
||||
/**
|
||||
* Server-assigned ids for signers created during this session, keyed by
|
||||
* their stable formId. The recipients form only learns real ids on a form
|
||||
* reset (step navigation), so this map bridges the gap between autosaves.
|
||||
*/
|
||||
const recipientIdByFormIdRef = useRef<Map<string, number>>(new Map());
|
||||
|
||||
/**
|
||||
* Merges known server-assigned ids into the outgoing payload. Without
|
||||
* this, a newly created signer (id-less in the form until the next form
|
||||
* reset) would be deleted and recreated on every autosave — churning ids,
|
||||
* signing tokens and the audit log. The formId doubles as the `clientId`
|
||||
* echoed back by the server.
|
||||
*/
|
||||
const withKnownRecipientIds = (localRecipients: SetRecipientsPayload) =>
|
||||
localRecipients.map((recipient) => ({
|
||||
...recipient,
|
||||
id: recipient.id ?? (recipient.formId ? recipientIdByFormIdRef.current.get(recipient.formId) : undefined),
|
||||
clientId: recipient.formId,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Records the ids of newly created recipients from the save response. A
|
||||
* ref — rather than writing ids back into the form — is deliberate: the
|
||||
* response can land mid interaction, and a form write here re-renders the
|
||||
* signer rows, which breaks an in-progress recipient drag.
|
||||
*/
|
||||
const rememberCreatedRecipientIds = (recipients: TSetEnvelopeRecipientsResponse['data']) => {
|
||||
for (const recipient of recipients) {
|
||||
if (recipient.clientId) {
|
||||
recipientIdByFormIdRef.current.set(recipient.clientId, recipient.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const registerExternalFlush = useCallback((key: string, flush: () => Promise<void>) => {
|
||||
externalFlushCallbacksRef.current.set(key, flush);
|
||||
|
||||
@@ -210,7 +256,7 @@ export const EnvelopeEditorProvider = ({
|
||||
triggerSave: setRecipientsDebounced,
|
||||
flush: flushSetRecipients,
|
||||
isPending: isRecipientsMutationPending,
|
||||
} = useEnvelopeAutosave(async (localRecipients: TSetEnvelopeRecipientsRequest['recipients']) => {
|
||||
} = useEnvelopeAutosave(async (localRecipients: SetRecipientsPayload) => {
|
||||
try {
|
||||
let recipients: TEditorEnvelope['recipients'] = [];
|
||||
|
||||
@@ -220,10 +266,12 @@ export const EnvelopeEditorProvider = ({
|
||||
const response = await setRecipientsMutation.mutateAsync({
|
||||
envelopeId: currentEnvelope.id,
|
||||
envelopeType: currentEnvelope.type,
|
||||
recipients: localRecipients,
|
||||
recipients: withKnownRecipientIds(localRecipients),
|
||||
});
|
||||
|
||||
recipients = response.data;
|
||||
|
||||
rememberCreatedRecipientIds(response.data);
|
||||
} else {
|
||||
recipients = mapLocalRecipientsToRecipients({ envelope: currentEnvelope, localRecipients });
|
||||
}
|
||||
@@ -252,7 +300,7 @@ export const EnvelopeEditorProvider = ({
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
const setRecipientsAsync = async (localRecipients: TSetEnvelopeRecipientsRequest['recipients']) => {
|
||||
const setRecipientsAsync = async (localRecipients: SetRecipientsPayload) => {
|
||||
setRecipientsDebounced(localRecipients);
|
||||
await flushSetRecipients();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user