Signing UI edge-case cleanup (#6849)

This commit is contained in:
EthanHealy01
2026-06-30 23:04:10 +01:00
committed by GitHub
parent bb92ecc143
commit 54042c8e5e
3 changed files with 28 additions and 2 deletions
@@ -130,7 +130,12 @@ const ShareManagementModal: React.FC<ShareManagementModalProps> = ({
}, [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<ShareManagementModalProps> = ({
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");
@@ -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
@@ -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<SigningRequestData | null>(
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<string | null>(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),