mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
fix: element visible race condition (#1996)
On larger documents we could accidentally start trying to render fields while not all pages of the PDF have loaded due to us checking for a single page existing. This would cause an error to be thrown, hard locking those documents. This change resolves this by grabbing the highest page number from the given fields and using it for the visibility check instead.
This commit is contained in:
@ -172,6 +172,8 @@ export const ConfigureFieldsView = ({
|
|||||||
name: 'fields',
|
name: 'fields',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const highestPageNumber = Math.max(...localFields.map((field) => field.pageNumber));
|
||||||
|
|
||||||
const onFieldCopy = useCallback(
|
const onFieldCopy = useCallback(
|
||||||
(event?: KeyboardEvent | null, options?: { duplicate?: boolean; duplicateAll?: boolean }) => {
|
(event?: KeyboardEvent | null, options?: { duplicate?: boolean; duplicateAll?: boolean }) => {
|
||||||
const { duplicate = false, duplicateAll = false } = options ?? {};
|
const { duplicate = false, duplicateAll = false } = options ?? {};
|
||||||
@ -540,7 +542,9 @@ export const ConfigureFieldsView = ({
|
|||||||
<div>
|
<div>
|
||||||
<PDFViewer documentData={normalizedDocumentData} />
|
<PDFViewer documentData={normalizedDocumentData} />
|
||||||
|
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
|
||||||
|
>
|
||||||
{localFields.map((field, index) => {
|
{localFields.map((field, index) => {
|
||||||
const recipientIndex = recipients.findIndex(
|
const recipientIndex = recipients.findIndex(
|
||||||
(r) => r.id === field.recipientId,
|
(r) => r.id === field.recipientId,
|
||||||
|
|||||||
@ -91,6 +91,8 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
localFields.filter((field) => field.inserted),
|
localFields.filter((field) => field.inserted),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const highestPendingPageNumber = Math.max(...pendingFields.map((field) => field.page));
|
||||||
|
|
||||||
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
|
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
|
||||||
|
|
||||||
const { mutateAsync: createDocumentFromDirectTemplate, isPending: isSubmitting } =
|
const { mutateAsync: createDocumentFromDirectTemplate, isPending: isSubmitting } =
|
||||||
@ -442,7 +444,9 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
|
||||||
|
>
|
||||||
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
||||||
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
|
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
|
||||||
<Trans>Click to insert field</Trans>
|
<Trans>Click to insert field</Trans>
|
||||||
|
|||||||
@ -50,8 +50,10 @@ export const EmbedDocumentFields = ({
|
|||||||
onSignField,
|
onSignField,
|
||||||
onUnsignField,
|
onUnsignField,
|
||||||
}: EmbedDocumentFieldsProps) => {
|
}: EmbedDocumentFieldsProps) => {
|
||||||
|
const highestPageNumber = Math.max(...fields.map((field) => field.page));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}>
|
||||||
{fields.map((field) =>
|
{fields.map((field) =>
|
||||||
match(field.type)
|
match(field.type)
|
||||||
.with(FieldType.SIGNATURE, () => (
|
.with(FieldType.SIGNATURE, () => (
|
||||||
|
|||||||
@ -106,6 +106,8 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
fields.filter((field) => field.inserted),
|
fields.filter((field) => field.inserted),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const highestPendingPageNumber = Math.max(...pendingFields.map((field) => field.page));
|
||||||
|
|
||||||
const { mutateAsync: completeDocumentWithToken, isPending: isSubmitting } =
|
const { mutateAsync: completeDocumentWithToken, isPending: isSubmitting } =
|
||||||
trpc.recipient.completeDocumentWithToken.useMutation();
|
trpc.recipient.completeDocumentWithToken.useMutation();
|
||||||
|
|
||||||
@ -465,7 +467,9 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
|
||||||
|
>
|
||||||
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
||||||
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
|
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
|
||||||
<Trans>Click to insert field</Trans>
|
<Trans>Click to insert field</Trans>
|
||||||
|
|||||||
@ -92,6 +92,8 @@ export const MultiSignDocumentSigningView = ({
|
|||||||
[],
|
[],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const highestPendingPageNumber = Math.max(...pendingFields.map((field) => field.page));
|
||||||
|
|
||||||
const uninsertedFields = document?.fields.filter((field) => !field.inserted) ?? [];
|
const uninsertedFields = document?.fields.filter((field) => !field.inserted) ?? [];
|
||||||
|
|
||||||
const onSignField = async (payload: TSignFieldWithTokenMutationSchema) => {
|
const onSignField = async (payload: TSignFieldWithTokenMutationSchema) => {
|
||||||
@ -357,7 +359,9 @@ export const MultiSignDocumentSigningView = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasDocumentLoaded && (
|
{hasDocumentLoaded && (
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
|
||||||
|
>
|
||||||
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
{showPendingFieldTooltip && pendingFields.length > 0 && (
|
||||||
<FieldToolTip
|
<FieldToolTip
|
||||||
key={pendingFields[0].id}
|
key={pendingFields[0].id}
|
||||||
|
|||||||
@ -79,6 +79,8 @@ export const DirectTemplateSigningForm = ({
|
|||||||
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
|
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const highestPageNumber = Math.max(...localFields.map((field) => field.page));
|
||||||
|
|
||||||
const fieldsRequiringValidation = useMemo(() => {
|
const fieldsRequiringValidation = useMemo(() => {
|
||||||
return localFields.filter((field) => isFieldUnsignedAndRequired(field));
|
return localFields.filter((field) => isFieldUnsignedAndRequired(field));
|
||||||
}, [localFields]);
|
}, [localFields]);
|
||||||
@ -221,7 +223,9 @@ export const DirectTemplateSigningForm = ({
|
|||||||
<DocumentFlowFormContainerHeader title={flowStep.title} description={flowStep.description} />
|
<DocumentFlowFormContainerHeader title={flowStep.title} description={flowStep.description} />
|
||||||
|
|
||||||
<DocumentFlowFormContainerContent>
|
<DocumentFlowFormContainerContent>
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
|
||||||
|
>
|
||||||
{validateUninsertedFields && uninsertedFields[0] && (
|
{validateUninsertedFields && uninsertedFields[0] && (
|
||||||
<FieldToolTip key={uninsertedFields[0].id} field={uninsertedFields[0]} color="warning">
|
<FieldToolTip key={uninsertedFields[0].id} field={uninsertedFields[0]} color="warning">
|
||||||
<Trans>Click to insert field</Trans>
|
<Trans>Click to insert field</Trans>
|
||||||
|
|||||||
@ -78,6 +78,8 @@ export const DocumentSigningPageView = ({
|
|||||||
const targetSigner =
|
const targetSigner =
|
||||||
recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;
|
recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;
|
||||||
|
|
||||||
|
const highestPageNumber = Math.max(...fields.map((field) => field.page));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DocumentSigningRecipientProvider recipient={recipient} targetSigner={targetSigner}>
|
<DocumentSigningRecipientProvider recipient={recipient} targetSigner={targetSigner}>
|
||||||
<div className="mx-auto w-full max-w-screen-xl sm:px-6">
|
<div className="mx-auto w-full max-w-screen-xl sm:px-6">
|
||||||
@ -224,7 +226,9 @@ export const DocumentSigningPageView = ({
|
|||||||
<DocumentSigningAutoSign recipient={recipient} fields={fields} />
|
<DocumentSigningAutoSign recipient={recipient} fields={fields} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible
|
||||||
|
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
|
||||||
|
>
|
||||||
{fields
|
{fields
|
||||||
.filter(
|
.filter(
|
||||||
(field) =>
|
(field) =>
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
|||||||
const documentVisibility = document?.visibility;
|
const documentVisibility = document?.visibility;
|
||||||
const currentTeamMemberRole = team.currentTeamRole;
|
const currentTeamMemberRole = team.currentTeamRole;
|
||||||
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
|
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
|
||||||
|
|
||||||
let canAccessDocument = true;
|
let canAccessDocument = true;
|
||||||
|
|
||||||
if (!isRecipient && document?.userId !== user.id) {
|
if (!isRecipient && document?.userId !== user.id) {
|
||||||
|
|||||||
@ -95,8 +95,10 @@ export const DocumentReadOnlyFields = ({
|
|||||||
setHiddenFieldIds((prev) => ({ ...prev, [fieldId]: true }));
|
setHiddenFieldIds((prev) => ({ ...prev, [fieldId]: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const highestPageNumber = Math.max(...fields.map((field) => field.page));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
<ElementVisible target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}>
|
||||||
{fields.map(
|
{fields.map(
|
||||||
(field) =>
|
(field) =>
|
||||||
!hiddenFieldIds[field.secondaryId] && (
|
!hiddenFieldIds[field.secondaryId] && (
|
||||||
|
|||||||
Reference in New Issue
Block a user