diff --git a/frontend/editor/src/core/components/shared/ShareManagementModal.tsx b/frontend/editor/src/core/components/shared/ShareManagementModal.tsx index 18c54ce342..2498f902cb 100644 --- a/frontend/editor/src/core/components/shared/ShareManagementModal.tsx +++ b/frontend/editor/src/core/components/shared/ShareManagementModal.tsx @@ -130,7 +130,12 @@ const ShareManagementModal: React.FC = ({ }, [config?.frontendUrl]); const loadShareLinks = useCallback(async () => { - if (!file.remoteStorageId) return; + if (!file.remoteStorageId) { + // No remote file yet — clear any leftover state from a previously opened file. + setShareLinks([]); + setSharedUsers([]); + return; + } setIsLoading(true); setErrorMessage(null); try { @@ -161,6 +166,10 @@ const ShareManagementModal: React.FC = ({ useEffect(() => { if (opened) { + // Clear the previous file's data before loading so a stale list is never + // shown (and never targeted by a Remove click) while the new file resolves. + setShareLinks([]); + setSharedUsers([]); loadShareLinks(); setActivityMap({}); setShareRole("editor"); diff --git a/frontend/editor/src/core/components/viewer/SignaturePreviewLayer.tsx b/frontend/editor/src/core/components/viewer/SignaturePreviewLayer.tsx index 0b6113d2d6..73479a826f 100644 --- a/frontend/editor/src/core/components/viewer/SignaturePreviewLayer.tsx +++ b/frontend/editor/src/core/components/viewer/SignaturePreviewLayer.tsx @@ -331,6 +331,12 @@ export const SignaturePreviewLayer = memo(function SignaturePreviewLayer({ newY = startTop + (startHeight - newHeight); } + // Keep the box on-page (mirrors the placement clamp). + newWidth = Math.min(newWidth, 1); + newHeight = Math.min(newHeight, 1); + newX = Math.max(0, Math.min(newX, 1 - newWidth)); + newY = Math.max(0, Math.min(newY, 1 - newHeight)); + onChange( previews.map((p) => p.id === preview.id diff --git a/frontend/editor/src/core/hooks/signing/useSigningSessionController.ts b/frontend/editor/src/core/hooks/signing/useSigningSessionController.ts index 2a2b001913..8fb1ddd5b6 100644 --- a/frontend/editor/src/core/hooks/signing/useSigningSessionController.ts +++ b/frontend/editor/src/core/hooks/signing/useSigningSessionController.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import apiClient from "@app/services/apiClient"; import { alert } from "@app/components/toast"; @@ -103,6 +103,10 @@ export function useSigningSessionController(enabled: boolean) { const [requestData, setRequestData] = useState( null, ); + // The session currently shown in the detail view. A refresh checks this at + // resolve time so a request dispatched before navigation is discarded rather + // than painting its data onto whatever is now on screen. + const openDetailSessionIdRef = useRef(null); // Leaving the tool (panel unmounts) must not leave the signing document and // overlays lingering on the shared viewer. @@ -111,6 +115,7 @@ export function useSigningSessionController(enabled: boolean) { }, [setOverlay]); const backToList = useCallback(() => { + openDetailSessionIdRef.current = null; setOverlay(null); setDetailData(null); setRequestData(null); @@ -203,6 +208,9 @@ export function useSigningSessionController(enabled: boolean) { ); const session = response.data; markSessionSeen(session.sessionId, countSignedParticipants(session)); + // Discard a refresh that resolves after the user navigated away, so we + // never paint this session's data onto another document. + if (openDetailSessionIdRef.current !== session.sessionId) return; setDetailData((prev) => (prev ? { ...prev, session } : prev)); // Keep the read-only overlay in sync as participants sign. setOverlay((prev) => @@ -275,6 +283,8 @@ export function useSigningSessionController(enabled: boolean) { detailResponse.data.myStatus === "NOTIFIED" || detailResponse.data.myStatus === "VIEWED"; + // Leaving the detail view: stop any in-flight detail refresh from applying. + openDetailSessionIdRef.current = null; // Seed the viewer with the document immediately; the request panel enriches // the overlay with interactive placement props once it mounts. setOverlay({ file: pdfFile }); @@ -353,6 +363,7 @@ export function useSigningSessionController(enabled: boolean) { } } + openDetailSessionIdRef.current = session.sessionId; setOverlay({ file: pdfFile, signaturePreviews: computeWetSignaturePreviews(detailResponse.data),