diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx index bc885d731..2b5bdebf3 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx @@ -6,7 +6,7 @@ import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; import { DocumentStatus, FieldType, RecipientRole } from '@prisma/client'; import { FileTextIcon, SparklesIcon } from 'lucide-react'; -import { Link, useRevalidator, useSearchParams } from 'react-router'; +import { useRevalidator, useSearchParams } from 'react-router'; import { isDeepEqual } from 'remeda'; import { match } from 'ts-pattern'; @@ -75,7 +75,7 @@ export const EnvelopeEditorFieldsPage = () => { const scrollableContainerRef = useRef(null); - const { envelope, editorFields, relativePath, editorConfig } = useCurrentEnvelopeEditor(); + const { envelope, editorFields, navigateToStep, editorConfig } = useCurrentEnvelopeEditor(); const { currentEnvelopeItem } = useCurrentEnvelopeRender(); @@ -172,10 +172,8 @@ export const EnvelopeEditorFieldsPage = () => { - )} diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx index 26bc5e5f8..ed294aacc 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx @@ -141,6 +141,7 @@ export const EnvelopeEditorRecipientForm = () => { }, { enabled: debouncedRecipientSearchQuery.length > 1, + retry: false, }, ); diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx index 600a1002a..aa8671f05 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx @@ -175,7 +175,7 @@ export const EnvelopeEditorSettingsDialog = ({ const { t, i18n } = useLingui(); const { toast } = useToast(); - const { envelope, updateEnvelopeAsync, editorConfig } = useCurrentEnvelopeEditor(); + const { envelope, updateEnvelopeAsync, editorConfig, isEmbedded } = useCurrentEnvelopeEditor(); const { settings } = editorConfig; @@ -286,11 +286,13 @@ export const EnvelopeEditorSettingsDialog = ({ setOpen(false); - toast({ - title: t`Success`, - description: t`Envelope updated`, - duration: 5000, - }); + if (!isEmbedded) { + toast({ + title: t`Success`, + description: t`Envelope updated`, + duration: 5000, + }); + } } catch (err) { const error = AppError.parseError(err); @@ -348,7 +350,7 @@ export const EnvelopeEditorSettingsDialog = ({ {/* Sidebar. */}
- + Document Settings diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx index 8a99c84ca..94d7ef689 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx @@ -8,7 +8,6 @@ import { DocumentStatus } from '@prisma/client'; import { FileWarningIcon, GripVerticalIcon, Loader2 } from 'lucide-react'; import { X } from 'lucide-react'; import { ErrorCode as DropzoneErrorCode, type FileRejection } from 'react-dropzone'; -import { Link } from 'react-router'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; import { @@ -17,6 +16,7 @@ import { } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app'; +import type { TEditorEnvelope } from '@documenso/lib/types/envelope-editor'; import { nanoid } from '@documenso/lib/universal/id'; import { PRESIGNED_ENVELOPE_ITEM_ID_PREFIX } from '@documenso/lib/utils/embed-config'; import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope'; @@ -53,7 +53,7 @@ export const EnvelopeEditorUploadPage = () => { const { maximumEnvelopeItemCount, remaining } = useLimits(); const { toast } = useToast(); - const { envelope, setLocalEnvelope, relativePath, editorFields, editorConfig, isEmbedded } = + const { envelope, setLocalEnvelope, editorFields, editorConfig, isEmbedded, navigateToStep } = useCurrentEnvelopeEditor(); const { envelopeItems: uploadConfig } = editorConfig; @@ -108,21 +108,23 @@ export const EnvelopeEditorUploadPage = () => { ); const onFileDrop = async (files: File[]) => { - const newUploadingFiles: (LocalFile & { file: File; data: Uint8Array | null })[] = - await Promise.all( - files.map(async (file) => { - return { - id: nanoid(), - envelopeItemId: isEmbedded ? `${PRESIGNED_ENVELOPE_ITEM_ID_PREFIX}${nanoid()}` : null, - title: file.name, - file, - isUploading: isEmbedded ? false : true, - // Clone the buffer so it can be read multiple times (File.arrayBuffer() consumes the stream once) - data: isEmbedded ? new Uint8Array((await file.arrayBuffer()).slice(0)) : null, - isError: false, - }; - }), - ); + const newUploadingFiles: (LocalFile & { + file: File; + data: TEditorEnvelope['envelopeItems'][number]['data'] | null; + })[] = await Promise.all( + files.map(async (file) => { + return { + id: nanoid(), + envelopeItemId: isEmbedded ? `${PRESIGNED_ENVELOPE_ITEM_ID_PREFIX}${nanoid()}` : null, + title: file.name, + file, + isUploading: isEmbedded ? false : true, + // Clone the buffer so it can be read multiple times (File.arrayBuffer() consumes the stream once) + data: isEmbedded ? new Uint8Array((await file.arrayBuffer()).slice(0)) : null, + isError: false, + }; + }), + ); setLocalFiles((prev) => [...prev, ...newUploadingFiles]); @@ -194,7 +196,9 @@ export const EnvelopeEditorUploadPage = () => { * Hide the envelope item from the list on deletion. */ const onFileDelete = (envelopeItemId: string) => { - setLocalFiles((prev) => prev.filter((uploadingFile) => uploadingFile.id !== envelopeItemId)); + setLocalFiles((prev) => + prev.filter((uploadingFile) => uploadingFile.envelopeItemId !== envelopeItemId), + ); const fieldsWithoutDeletedItem = envelope.fields.filter( (field) => field.envelopeItemId !== envelopeItemId, @@ -476,13 +480,10 @@ export const EnvelopeEditorUploadPage = () => { {/* Recipients Section */} - {editorConfig.general.allowAddFieldsStep && (
-
)} diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor.tsx index dbed47b8b..a35bfc1fd 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor.tsx @@ -22,6 +22,7 @@ import { useNavigate, useSearchParams } from 'react-router'; import { Link } from 'react-router'; import { match } from 'ts-pattern'; +import type { EnvelopeEditorStep } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope'; import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out'; @@ -43,8 +44,6 @@ import EnvelopeEditorHeader from './envelope-editor-header'; import { EnvelopeEditorPreviewPage } from './envelope-editor-preview-page'; import { EnvelopeEditorUploadPage } from './envelope-editor-upload-page'; -type EnvelopeEditorStep = 'upload' | 'addFields' | 'preview'; - type EnvelopeEditorStepData = { id: string; title: MessageDescriptor; @@ -83,14 +82,12 @@ export const EnvelopeEditor = () => { editorConfig, isDocument, isTemplate, - isAutosaving, - flushAutosave, relativePath, syncEnvelope, + navigateToStep, } = useCurrentEnvelopeEditor(); const [searchParams, setSearchParams] = useSearchParams(); - const [isStepLoading, setIsStepLoading] = useState(false); const { general: { @@ -130,47 +127,25 @@ export const EnvelopeEditor = () => { })); }, [editorConfig]); - const [currentStep, setCurrentStep] = useState(() => { - const searchParamStep = searchParams.get('step') as EnvelopeEditorStep | undefined; + const [currentStep, setCurrentStep] = useState<{ step: EnvelopeEditorStep; isLoading: boolean }>( + () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const searchParamStep = searchParams.get('step') as EnvelopeEditorStep | undefined; - // Empty URL param equals upload, otherwise use the step URL param - if (!searchParamStep) { - return 'upload'; - } + // Empty URL param equals upload, otherwise use the step URL param + if (!searchParamStep) { + return { step: 'upload', isLoading: false }; + } - const validSteps: EnvelopeEditorStep[] = ['upload', 'addFields', 'preview']; + const validSteps: EnvelopeEditorStep[] = ['upload', 'addFields', 'preview']; - if (validSteps.includes(searchParamStep)) { - return searchParamStep; - } + if (validSteps.includes(searchParamStep)) { + return { step: searchParamStep, isLoading: false }; + } - return 'upload'; - }); - - const navigateToStep = (step: EnvelopeEditorStep) => { - setCurrentStep(step); - - void flushAutosave(); - - if (!isStepLoading && isAutosaving) { - setIsStepLoading(true); - } - - // Update URL params: empty for upload, otherwise set the step - if (step === 'upload') { - setSearchParams((prev) => { - const newParams = new URLSearchParams(prev); - newParams.delete('step'); - return newParams; - }); - } else { - setSearchParams((prev) => { - const newParams = new URLSearchParams(prev); - newParams.set('step', step); - return newParams; - }); - } - }; + return { step: 'upload', isLoading: false }; + }, + ); // Watch the URL params and setStep if the step changes. useEffect(() => { @@ -178,20 +153,19 @@ export const EnvelopeEditor = () => { const foundStep = envelopeEditorSteps.find((step) => step.id === stepParam); - if (foundStep && foundStep.id !== currentStep) { + if (foundStep && foundStep.id !== currentStep.step) { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - navigateToStep(foundStep.id as EnvelopeEditorStep); + void navigateToStep(foundStep.id as EnvelopeEditorStep).then(() => { + setCurrentStep({ + step: foundStep.id as EnvelopeEditorStep, + isLoading: false, + }); + }); } }, [searchParams]); - useEffect(() => { - if (!isAutosaving) { - setIsStepLoading(false); - } - }, [isAutosaving]); - const currentStepData = - envelopeEditorSteps.find((step) => step.id === currentStep) || envelopeEditorSteps[0]; + envelopeEditorSteps.find((step) => step.id === currentStep.step) || envelopeEditorSteps[0]; return (
@@ -285,11 +259,12 @@ export const EnvelopeEditor = () => { > {envelopeEditorSteps.map((step) => { const Icon = step.icon; - const isActive = currentStep === step.id; + const isActive = currentStep.step === step.id; return ( + )} +
+ ))} +
+ + +
+ Raw CSS +