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:
Lucas Smith
2025-08-26 11:08:43 +10:00
committed by GitHub
parent 44f5da95b3
commit 4012022f55
9 changed files with 37 additions and 8 deletions

View File

@ -172,6 +172,8 @@ export const ConfigureFieldsView = ({
name: 'fields',
});
const highestPageNumber = Math.max(...localFields.map((field) => field.pageNumber));
const onFieldCopy = useCallback(
(event?: KeyboardEvent | null, options?: { duplicate?: boolean; duplicateAll?: boolean }) => {
const { duplicate = false, duplicateAll = false } = options ?? {};
@ -540,7 +542,9 @@ export const ConfigureFieldsView = ({
<div>
<PDFViewer documentData={normalizedDocumentData} />
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
>
{localFields.map((field, index) => {
const recipientIndex = recipients.findIndex(
(r) => r.id === field.recipientId,

View File

@ -91,6 +91,8 @@ export const EmbedDirectTemplateClientPage = ({
localFields.filter((field) => field.inserted),
];
const highestPendingPageNumber = Math.max(...pendingFields.map((field) => field.page));
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
const { mutateAsync: createDocumentFromDirectTemplate, isPending: isSubmitting } =
@ -442,7 +444,9 @@ export const EmbedDirectTemplateClientPage = ({
</div>
</div>
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
>
{showPendingFieldTooltip && pendingFields.length > 0 && (
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
<Trans>Click to insert field</Trans>

View File

@ -50,8 +50,10 @@ export const EmbedDocumentFields = ({
onSignField,
onUnsignField,
}: EmbedDocumentFieldsProps) => {
const highestPageNumber = Math.max(...fields.map((field) => field.page));
return (
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}>
{fields.map((field) =>
match(field.type)
.with(FieldType.SIGNATURE, () => (

View File

@ -106,6 +106,8 @@ export const EmbedSignDocumentClientPage = ({
fields.filter((field) => field.inserted),
];
const highestPendingPageNumber = Math.max(...pendingFields.map((field) => field.page));
const { mutateAsync: completeDocumentWithToken, isPending: isSubmitting } =
trpc.recipient.completeDocumentWithToken.useMutation();
@ -465,7 +467,9 @@ export const EmbedSignDocumentClientPage = ({
</div>
</div>
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
>
{showPendingFieldTooltip && pendingFields.length > 0 && (
<FieldToolTip key={pendingFields[0].id} field={pendingFields[0]} color="warning">
<Trans>Click to insert field</Trans>

View File

@ -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 onSignField = async (payload: TSignFieldWithTokenMutationSchema) => {
@ -357,7 +359,9 @@ export const MultiSignDocumentSigningView = ({
</div>
{hasDocumentLoaded && (
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPendingPageNumber}"]`}
>
{showPendingFieldTooltip && pendingFields.length > 0 && (
<FieldToolTip
key={pendingFields[0].id}

View File

@ -79,6 +79,8 @@ export const DirectTemplateSigningForm = ({
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const highestPageNumber = Math.max(...localFields.map((field) => field.page));
const fieldsRequiringValidation = useMemo(() => {
return localFields.filter((field) => isFieldUnsignedAndRequired(field));
}, [localFields]);
@ -221,7 +223,9 @@ export const DirectTemplateSigningForm = ({
<DocumentFlowFormContainerHeader title={flowStep.title} description={flowStep.description} />
<DocumentFlowFormContainerContent>
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
>
{validateUninsertedFields && uninsertedFields[0] && (
<FieldToolTip key={uninsertedFields[0].id} field={uninsertedFields[0]} color="warning">
<Trans>Click to insert field</Trans>

View File

@ -78,6 +78,8 @@ export const DocumentSigningPageView = ({
const targetSigner =
recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;
const highestPageNumber = Math.max(...fields.map((field) => field.page));
return (
<DocumentSigningRecipientProvider recipient={recipient} targetSigner={targetSigner}>
<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} />
)}
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible
target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}
>
{fields
.filter(
(field) =>

View File

@ -67,6 +67,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
const documentVisibility = document?.visibility;
const currentTeamMemberRole = team.currentTeamRole;
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
let canAccessDocument = true;
if (!isRecipient && document?.userId !== user.id) {

View File

@ -95,8 +95,10 @@ export const DocumentReadOnlyFields = ({
setHiddenFieldIds((prev) => ({ ...prev, [fieldId]: true }));
};
const highestPageNumber = Math.max(...fields.map((field) => field.page));
return (
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
<ElementVisible target={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${highestPageNumber}"]`}>
{fields.map(
(field) =>
!hiddenFieldIds[field.secondaryId] && (